多写存储指南
本指南介绍如何配置 OpenViking 的多写存储能力。多写存储允许一个 primary 后端同时复制写入多个 backup 后端,用于高可用、跨区域副本、读加速和存储迁移。
多写能力位于 RAGFS 内部。OpenViking 的 Python SDK、HTTP API 和 CLI 使用方式保持不变。
前置条件
- 已有可用的
ov.conf。 - 已确认 primary backend 可以正常读写。
- 如果要接入 S3 兼容存储,已准备好 bucket、endpoint 和访问凭据。
- 如果要迁移已有数据,先完成存量数据迁移,再启用多写。
最小配置
下面示例使用本地目录作为 primary,并把写入复制到另一个本地目录。
{
"storage": {
"workspace": "./data",
"agfs": {
"backend": "local",
"backups": {
"sync_type": "async",
"items": [
{
"name": "local-backup",
"backend": "local",
"local": {
"workspace": "./data/backup"
}
}
]
}
}
}
}说明:
- 顶层
backend是 primary。 backups.items[]是 backup 列表。name是 backup 的稳定身份,后续同步元数据会引用它。backend = "local"的 backup 使用local.workspace指定本地目录。sync_type不配置时默认按异步模式理解。
多 Backup 配置
可以配置多个 backup。下面示例同时写入本地副本和 S3 兼容对象存储。
{
"storage": {
"workspace": "./data",
"agfs": {
"backend": "local",
"backups": {
"sync_type": "async",
"items": [
{
"name": "local-az2",
"backend": "local",
"local": {
"workspace": "./data/local-az2"
}
},
{
"name": "object-store",
"backend": "s3",
"s3": {
"bucket": "openviking-backup",
"region": "us-east-1",
"endpoint": "https://s3.example.com",
"access_key": "your-access-key",
"secret_key": "your-secret-key",
"prefix": "openviking",
"directory_marker_mode": "none"
}
}
]
}
}
}
}建议:
name不要使用会频繁变化的机器名或临时编号。- backup 的底层路径或 bucket 应避免与 primary 指向同一物理位置。
- 修改 backup
name会影响历史同步元数据的识别,生产环境应谨慎变更。
S3 兼容存储注意事项
使用 S3 兼容服务(MinIO、RustFS、Ceph 等)时,s3 段需要额外配置以下字段:
| 字段 | 是否必填 | 说明 |
|---|---|---|
use_path_style | 大多数 S3 兼容服务必填 | 设置为 true 使用路径风格 URL(http://host/bucket/key)。大多数 S3 兼容服务需要此配置。 |
directory_marker_mode | S3 兼容服务必填 | 必须显式设置为 "none"。如果不配置,RAGFS Rust binding 启动时会报 AGFSConfigError: invalid directory_marker_mode: null 并静默崩溃。 |
use_ssl | 可选 | HTTP 端点(如 http://localhost:9000)需要设置为 false。 |
S3 兼容存储最小示例(RustFS/MinIO):
{
"name": "s3-backup",
"backend": "s3",
"s3": {
"bucket": "my-bucket",
"endpoint": "http://localhost:9000",
"access_key": "your-access-key",
"secret_key": "your-secret-key",
"prefix": "openviking",
"use_ssl": false,
"use_path_style": true,
"directory_marker_mode": "none"
}
}为什么需要
directory_marker_mode?S3 兼容存储服务对"目录"的处理方式与 AWS S3 不同。RAGFS Rust binding 必须知道创建目录时是否需要写入目录标记对象。合法取值为
"none"、"empty"和"nonempty"。对于不使用目录标记的 S3 兼容服务(RustFS、MinIO、Ceph 等),设置为"none"。如果省略,Rust binding 默认值为null(不合法),导致服务端在启动时静默崩溃,报错AGFSConfigError: invalid directory_marker_mode: null。
Docker 网络配置
在 Docker 中运行 OpenViking 并配置同主机的 S3 备份时,需要注意:
- Linux Docker:使用
--network host或宿主机局域网 IP。Docker bridge 网络可通过网关 IP(如172.17.0.1:9000)访问宿主机局域网。 - macOS/Windows Docker Desktop:
--network host不支持。S3 端点使用host.docker.internal(映射为宿主机的 localhost),或使用宿主机局域网 IP。
如果启用 S3 备份后服务静默崩溃,请优先排查 Docker 网络。RAGFS Rust binding 在容器内无法访问 S3 端点时会报 dispatch failure 错误。
同步模式选择
异步模式
异步模式适合大多数场景。
{
"backups": {
"sync_type": "async",
"items": []
}
}特点:
- primary 写入成功后立即返回。
- backup 写入在后台执行。
- 写入延迟低。
- backup 可能短暂落后。
适合:
- 写入吞吐优先。
- backup 主要用于灾备。
- 可以接受最终一致性。
同步模式
同步模式会等待 backup 确认。
{
"backups": {
"sync_type": "sync",
"write_ack_count": 1,
"write_ack_timeout_ms": 5000,
"items": []
}
}参数说明:
| 参数 | 说明 |
|---|---|
write_ack_count | 写入返回前至少需要多少个 backup 确认 |
write_ack_timeout_ms | 等待 backup 确认的超时时间,单位毫秒 |
特点:
- 写入确认更强。
- 写入延迟受 backup 影响。
- 未确认的 backup 会继续由后台重试修复。
- primary 已写成功但 backup 未达确认数时,客户端可能收到错误;此时 primary 中可能已经存在数据。
适合:
- 希望尽量减少 primary 与 backup 的确认窗口。
- backup 延迟可控。
- 调用方能接受同步写入带来的额外延迟。
配置读加速
backup 默认不参与读取。要让 backup 服务读取,需要显式配置 operations。
{
"name": "cache-backend",
"backend": "memfs",
"operations": [
{
"operation": "read",
"priority": 10
}
]
}读取优先级规则:
priority越小越优先。- 只有声明
read的 backup 才参与读取。 - primary 始终作为最终兜底。
- 冷备 backup 不建议配置读能力。
如果一个 backup 只配置了 read,没有配置 write,它不会接收普通多写复制。只有在你明确知道该 backend 的数据来源时,才应使用这种配置。
Redirect 配置
Redirect 用于把匹配的文件写入指定 backup,而不是写入 primary。
按扩展名重定向:
{
"storage": {
"agfs": {
"backend": "local",
"redirects": [
{
"type": "FileExtensionPolicy",
"extensions": ["(pdf|ppt|zip)"],
"target": ["object-store"]
}
],
"backups": {
"items": [
{
"name": "object-store",
"backend": "s3",
"s3": {
"bucket": "openviking-large-files",
"endpoint": "https://s3.example.com"
}
}
]
}
}
}
}按大小重定向:
{
"type": "FileOverSizePolicy",
"max_size_mb": 100,
"target": ["object-store"]
}注意:
target必须引用已有 backup 的name。- redirect 文件仍会通过普通 API 呈现为可读、可列举、可查询状态。
- redirect 映射保存在 primary 的内部元数据中。
Exclude 配置
Exclude 用于让某个 backup 跳过匹配文件。
{
"name": "cache-backend",
"backend": "memfs",
"excludes": [
{
"type": "FileOverSizePolicy",
"max_size_mb": 50
},
{
"type": "FileExtensionPolicy",
"extensions": ["(mp4|zip)"]
}
]
}常见用法:
- 缓存 backend 排除大文件。
- 低成本备份排除无需保存的文件类型。
- 某个 backup 只保存文本或配置类资源。
如果 redirect 的目标 backup 同时 exclude 了该文件,说明配置互相冲突。请优先修正配置,不要依赖系统自动猜测其他目标。
加密配置
多写存储复用 OpenViking 的透明静态加密能力。
全局加密开启示例:
{
"encryption": {
"enabled": true,
"provider": "local",
"local": {
"key_file": "~/.openviking/master.key"
}
},
"storage": {
"workspace": "./data",
"agfs": {
"backend": "local",
"backups": {
"items": [
{
"name": "plain-cache",
"backend": "memfs",
"encryption": {
"enabled": false
}
},
{
"name": "encrypted-backup",
"backend": "local",
"local": {
"workspace": "./data/encrypted-backup"
},
"encryption": {
"enabled": true
}
}
]
}
}
}
}规则:
- 全局
encryption.enabled=true时,primary 必须加密。 - backup 可以通过
encryption.enabled单独控制是否加密。 - Python SDK、HTTP API 和 CLI 不需要处理加解密。
.redirect.json和.sync_log.json等内部元数据会跟随 primary 加密策略。
存量数据迁移
多写只复制启用之后的新写入,不会自动复制历史文件。
推荐迁移流程:
- 停止或冻结写入窗口。
- 使用 OVPack 或其他受控工具把存量数据迁移到目标 backup。
- 校验目标 backend 的数据完整性。
- 配置并启用
storage.agfs.backups。 - 恢复写入。
- 观察同步状态和错误日志。
如果无法冻结写入,可以先做一次全量迁移,再短暂停写做增量校验,最后启用多写。
验证配置
启动前建议运行:
openviking-server doctor启动后可以用普通文件 API 验证:
openviking write viking://resources/multiwrite-check.txt \
--content "multi-write check" \
--wait
openviking read viking://resources/multiwrite-check.txt如果使用本地 backup,可以直接检查 backup 目录中是否出现对应文件。生产环境更推荐使用系统健康检查和同步状态命令。
常见问题
为什么 backup 没有参与读取?
backup 默认只参与写入,不参与读取。需要在 backup 上显式配置:
{
"operations": [
{
"operation": "read",
"priority": 10
}
]
}为什么启用多写后历史文件没有出现在 backup?
多写只处理启用后的新写入。历史文件需要先通过 OVPack、对象存储复制或后续 backfill 能力迁移。
异步模式下能否保证立即读到 backup 的最新数据?
不能。异步模式只保证最终一致。需要强读一致时,应让读取回退到 primary,或避免让可能滞后的 backup 参与读路由。
内部元数据文件会出现在用户列表里吗?
不会。.redirect.json 和 .sync_log.json 是内部文件,会被普通目录列表隐藏。
sync 模式返回失败是否表示 primary 一定没写入?
不是。primary 写成功但 backup 未达到确认数时,客户端可能收到失败。此时 primary 数据可能已经存在,落后的 backup 会由后台重试修复。
