一条容易被读错的新闻

9 月 14 日,Perplexity 的工程博客发了一篇技术长文,讲他们怎么把搜索链路里的读取层换掉。两三天后,公司账号在社交平台上继续拆解架构细节,讨论才真正扩散开。

传播最广的版本是「两个工程师加数百个智能体,两个月写出一个数据库,一年省下一亿美元」。这三个数字里,前两个有出处,第三个需要打个折。

先把事实摆出来。CobbleDB 是一套约 4 万行的 Rust 键值存储,用来承接搜索时的批量读取,替换掉原来托在 DynamoDB 上的那一层。开发过程中用到了数百个常驻的编码智能体,但架构设计、代码评审与生产授权都留在两名工程师手里。

那篇博客里有一句话被多家媒体单独拎出来当标题:智能体帮忙把数据库造出来了,却没被允许运行它。

三层存储各管一段

要理解这次改动的价值,得先看清分工。旧架构把文档处理和线上读取耦合在同一套数据库上,新架构把这条链路拆成三段。

Perplexity 工程博客 · 9 月 14 日写入侧(与线上读取解耦)Pillar持久文档状态与发布决策,元数据/分块/向量分表族放在 YTsaurusLorry把导出物切成按分区组织的批次写入 S3,并登记到 PostgreSQL 控制面CobbleDB热存储:键为哈希后的 URL,值为分块文本与各自的向量读取侧:路由器把键映射到分区并行读取,优先同可用区副本;副本慢则改读另一份。每分区三副本,节点走 RocksDB MultiGet。
写入与读取被拆成两条独立的节奏:副本各自按批次推进,落后的副本不会拖住其他副本,这是「一两天跑一遍全库回填」变得可行的前提。

Pillar 负责持久状态与发布决策,页面元数据、分块与向量分别放在 YTsaurus 的不同表族里,事务要么三件事一起做成,要么什么都不做。Lorry 把 Pillar 的导出物切成按分区组织的批次写进对象存储,并登记到一个 PostgreSQL 控制面。CobbleDB 是热存储,键是哈希后的地址,值是预处理好的页面表示,包含分块文本与各自的向量。

读取路径的设计目标很窄:给一批页面标识,尽快把内容还回来。路由器把键映射到分区并行读取,优先选同可用区的副本;如果某个副本慢,就去找另一份。每个分区三副本,节点用 RocksDB 的 MultiGet 批量取,命中缓存走内存,未命中走本地 NVMe。

这套分工里,真正关键的不是某个组件,而是把写入与读取的节奏解耦。旧架构下,处理管道直接往热存储写,一次大规模重跑就变成一波逐条更新,处理任务必须迁就热存储的吞吐与重试行为。作者提到,过去没办法用一两天跑一遍全库的批量计算来试新的分块方法或更换嵌入模型。拆开之后,副本可以各自按自己的节奏追批次,落后的副本不会拖住别人。

这也是一个容易被忽略的取舍:加一层批次投递,代价是更新到达线上会有一段时间差;换来的是处理任务与线上请求互不干扰。能不能接受这个时间差,取决于业务对内容新鲜度的要求。

数字与口径

单位:毫秒 · 对照使用不同时间点的线上流量,非配对实验分位DynamoDBCobbleDB倍数p5031.45.605.61 倍p9056.79.775.80 倍p9912324.25.08 倍生产对照时约每秒 20 万次请求;压测到每秒 50 万次后才出现性能下降。成本侧为内部模型估算的「至少省 20%」。
三个分位的改善幅度接近,说明这不是把长尾压平,而是整体前移。「一年最多省 1 亿美元」是前瞻数字,公司负责人已明确表示那不是当下支出。

公司给出的对照是:迁移之后中位批读延迟 5.60 毫秒、p90 为 9.77 毫秒、p99 为 24.2 毫秒;迁移前在托管数据库上分别是 31.4、56.7 与 123 毫秒,折算下来是五倍上下的改善。压测把吞吐拉到每秒 50 万次请求后才开始出现下降,生产对照时的量级是每秒约 20 万次。

成本一侧,公司说的是内部成本模型估算相对原方案至少省 20%。被反复引用的「一年最多省 1 亿美元」是另一个量级的前瞻数字,公司负责人在后续回复里明确表示那不是当下的支出口径。

还有一处必须标注:这次对照用的是不同时间点的线上流量,不是同一批请求的配对实验。因此这组数字衡量的是热存储的批读延迟,不是用户感知的端到端答案延迟。想知道换成别的负载会怎样,只能自己测。

智能体做了什么,没做什么

这条新闻真正值得记下来的是权限的切法。

智能体参与了代码生成、测试与跨会话的问题定位,这让两个人的产出顶上了一支队伍的量级。但边界画得清楚:架构决策、评审与生产授权不在它们手里。CMU 的一位数据库方向教授此前在行业会议上讲过一句被多次引用的话——数据库是智能体最难、也最重要的挑战之一,因为涉及生产数据的错误往往难以甚至无法回滚。而「无法回滚」恰好是自治智能体最不擅长处理的场景。

这个切法可以推广到别处:让智能体做产出可被验证、可被回滚的部分;把不可逆的那一步留给有责任主体的人。技术上是保守的,工程上是务实的,因为它把「快」与「不可挽回」分到了两条轨道上。

顺带一个细节:Perplexity 表示后续会把 CobbleDB 开源。如果真开源,这套架构会变成可复用的参考,而不只是一家公司的内部选择。到目前为止官方尚未公布开源时间表。

为什么托管数据库在这里不合适

把这件事读成「托管数据库不行」是过度解读,但它确实说明了适配边界在哪里。

原因有三层。其一是计价方式:按读写字节计费,而一次搜索请求涉及 100 到 120 个页面键,拆成每批 10 到 20 条、单条约 50KB,长期重复读取这些记录会把容量吃掉一大块;爬取与重跑又在持续产生写。其二是调优粒度:需要的能力很窄,只是「给一批标识、快速返回预处理内容」,但自己想控制哪台机器持有分区、给缓存多少内存、由哪个副本响应请求,这些在托管服务里做不到,而未命中缓存、跨区跳转、副本变慢都会拖住整批。其三是耦合:处理管道直写热存储,导致大规模回填困难。

三个理由合起来指向一个判断:当读取模式足够窄、足够固定,自建专用存储的收益可能超过托管服务带来的省心。但这条判断有前提——你得先把自己的读取模式收敛到足够窄,否则自建只是把复杂度从服务商搬回自己家里。

可以带走的四条

其一,把不可逆操作与智能体产出分开。这条比任何提示词技巧都重要。

其二,用「批读 p99」这类窄指标衡量存储改动,而不是笼统的「更快了」。窄指标的好处是可以断言、可以回归。

其三,把写入与读取的节奏解耦当成通用原则,不只适用于数据库。凡是「处理任务会阻塞线上请求」的地方,都值得考虑加一层批次投递。

其四,看到自报的倍数时,先找两件事:基线是什么,以及对照组是不是同一批请求。

这类改造适合谁

把这套做法抄回自己项目之前,有几条前置条件值得先对照。

一是读取模式要足够窄且足够固定。这里的目标窄到只剩一句话——给一批标识、返回预处理好的内容。目标越窄,专用实现能压榨出的空间越大;如果读取需求经常变,专用存储会变成维护负担。

二是数据规模与查询量要持续增长。按用量计费的托管服务在规模小的时候更便宜,这个结论只在规模大到一定程度、而且成本曲线随规模上升时才翻转。用量有限时,省下的钱覆盖不了自建投入。

三是要有人长期维护。分布式存储的故障演练、扩缩容、版本升级与人员交接都是持续支出,这部分不会出现在任何一张性能对照表上。

四是能接受更新延迟。写入走批次投递意味着内容更新不是即时的,业务如果需要秒级可见,这条架构就不适用。

四条里只要有一条不满足,收益就可能被隐性成本吃掉。这也是为什么同样的做法在别处不一定成立——它不是一条「自建更好」的结论,而是一次条件明确时的选择。

需要标注的边界

其一,全部性能与成本数字来自公司自报,未见独立复现,也未公开完整测算方法。

其二,对照使用不同时间点的线上流量,属于热存储层面的比较,不能直接外推成用户体验改善。

其三,这套设计对「读取模式高度固定、语料持续增长」的场景贴得很紧。迁移到读取模式经常变化、或写读比完全不同的系统上,收益可能明显缩水。

其四,自建存储的隐性成本不会体现在延迟表里:长期维护、故障演练、扩容与人员交接都要自己承担。那篇博客没有讨论这一侧。

目前官方及行业暂未披露更多细节,后续将持续跟进迭代动态。