源码解读 · 第 5 篇
让 AI 记忆存在 S3 / 本地磁盘 / SQL / 内存
OPC
云与数字化
1 人公司 · LLM 协作 · 工程底座
+ 关注
核心判断:ObjectStore trait 用三个异步方法(put/get/exists)屏蔽底层存储差异,四种后端无缝切换。多租户隔离通过 account 路径前缀实现,既简单又安全。
📚 OpenViking 源码解读
#1 架构解析 · #2 Git 对象存储 · #3 Fast Path · #4 PyO3 桥接 · #5 多后端架构(本文)
01为什么需要多后端?
AI 记忆系统存储需求多样:本地开发需要零网络开销,生产环境需要跨机器共享,大规模部署需要 S3 的弹性,嵌入式设备只需要内存存储。OpenViking 的解法是:定义统一 trait 接口,四种实现满足不同需求。
🖥️ 本地:开发调试、单机部署、零网络开销 ☁️ S3:大规模生产、多 Agent 集群、弹性扩展 📊 SQL:需要事务保证、审计日志、行级权限 💾 内存:嵌入式设备、测试环境、零 IO
02ObjectStore trait:统一存储接口
所有后端实现同一个 trait,三个方法含义清晰:
#[async_trait] pub trait ObjectStore: Send + Sync + 'static {
async fn put(&self, account: &str, oid: &ObjectId, zlib_body: Bytes) -> Result<()>;
async fn get(&self, account: &str, oid: &ObjectId) -> Result<Bytes>;
async fn exists(&self, account: &str, oid: &ObjectId) -> Result<bool>;
}put:写入 Git 对象(已 zlib 压缩),account 隔离租户 get:读取 Git 对象,返回压缩原始字节 exists:Fast Path 3 前置检查,避免重复写入 S3 请求
"
最小接口原则:只需三个方法就完整抽象存储系统所有操作,任何新后端实现这三个方法即可无缝接入。
03四种后端对比
后端 | 介质 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|---|
LocalObjectStore | 本地磁盘 | 开发调试 | 零开销 | 不跨机器 |
S3ObjectStore | S3/TOS | 生产集群 | 弹性扩展 | 计费成本 |
SqlObjectStore | SQLite/PG | 事务场景 | 事务+查询 | 大对象低效 |
MemoryObjectStore | 内存 | 嵌入式/测试 | 最快 | 重启丢失 |
04LocalObjectStore:本地磁盘实现
路径格式:Git loose object 标准,两级目录避免单目录超过 10000 文件。格式:{account}/objects/{aa}/{bb...}
// 路径格式:{account}/objects/{aa}/{bb...}
base_dir/{account}/objects/{aa}/{bb...}
// aa = OID 前2位(子目录,避免单目录 >10000 文件)
// bb = OID 剩余38位(文件名)put 操作:写临时文件再 rename。POSIX 下 rename 是原子的——要么完全成功,要么目标文件不存在。
// 原子写入:写临时文件再 rename
let tmp = path.with_extension(format!("tmp.{}.{}", pid, seq));
await tokio::fs::write(&tmp, &zlib_body)?;
await tokio::fs::rename(&tmp, &path)?; // POSIX 原子put 操作信任调用方传入的 oid(SHA-1 哈希)。如果 oid 错误,对象存储在错误地址,下次 get 找不到。
05S3ObjectStore:对象存储后端
S3 的存储模型天然契合 Git 内容寻址。S3ObjectStore 直接调用 AWS S3 SDK:
put → PutObject · get → GetObject · exists → HeadObject
S3 按请求计费。Fast Path 3 的 exists precheck 就是为降低 S3 成本——写前先查是否存在,避免重复写入。
06SqlObjectStore & MemoryObjectStore
SqlObjectStore:支持 SQLite(单节点)和 PostgreSQL(多 Agent 集群),切换只需改数据库连接 URL,不改代码。适合审计日志、多租户行级权限控制。
MemoryObjectStore:用 DashMap(并发 HashMap)存储:
struct MemoryObjectStore {
objects: DashMap<(String, ObjectId), Bytes>,
}适用测试环境、嵌入式设备、短期缓存。三大限制:重启丢失、无法跨进程、受内存限制。
07多租户隔离:account 前缀
所有方法第一个参数都是 account: &str,参与存储路径构造:
本地:{base}/{account}/objects/...
S3:{bucket}/{account}/objects/...
SQL:WHERE account = ?
内存:(account, oid) 作为 HashMap key
account 隔离在存储层实现,而非业务层。无论哪种后端,租户隔离都内建于存储层,业务代码只传 account。
08后端选型决策树
单机开发/快速原型? → LocalObjectStore,零配置 跨机器共享? → S3ObjectStore 或 SqlObjectStore 数据量大/流量高? → S3ObjectStore,弹性扩展 需要事务保证? → SqlObjectStore + PostgreSQL 嵌入式/资源受限? → MemoryObjectStore 测试环境? → MemoryObjectStore 或 MockObjectStore
· · ·
核心洞察
ObjectStore trait 最小接口(3 方法)+ 路径格式统一({account}/objects/{aa}/{bb})+ 存储层多租户隔离 = 后端切换不改代码,数据迁移不需路径转换。
09设计总结
设计要点 | OpenViking 做法 | 工程价值 |
|---|---|---|
接口抽象 | ObjectStore trait,3 方法 | 后端可插拔 |
路径格式统一 | {account}/objects/{aa}/{bb...} | 数据迁移无需转换 |
多租户隔离 | account 透传所有操作 | 存储层内建安全 |
幂等写入 | rename 失败 → exists → 幂等 | 分布式并发安全 |
Fast Path | exists precheck | 降低 S3 请求成本 |
行动建议
本周 clone OpenViking,用 LocalObjectStore 跑通 Git 对象存取全流程 本月 调研 S3ObjectStore 接入 TOS(字节火山引擎),测写放大防护效果 永远 设计任何新存储层,优先抽象 trait 接口,避免绑定特定后端
END · 完