文章从生产负载出发,分析向量数据规模、并发检索与批量写入带来的延迟、内存占用和索引可用性问题。重点揭示小规模验证无法暴露的 RAG 基础设施成本。
一个只存储数字的系统,为什么会成为你运行成本最高的系统之一
刚开始使用 Vector Database 时,感觉它根本不值一提。几千个 vector、一个节点,查询不到 100 毫秒就能返回。我记得当时觉得,这是整个 pipeline 中最轻松的部分——数据提取有 bug,分块需要权衡,metadata 也有缺失,但 Vector Database 就是……能用。存入一个 vector,搜索一个 vector,结束。
随后,真正的生产负载来了。不是演示环境,不是测试集,而是真实的文档规模、真实的查询流量,并且系统不再只是短时间突发运行,而是持续不断地运转。
最先缓慢上升的是延迟。过去能立即返回的查询,开始需要明显更长的时间,尤其是在数据摄取任务与线上流量同时运行的时段。接着,内存占用开始增长,而且增长幅度与我以为自己新增的数据量并不匹配。后来有一次批量更新——添加一批新文档——让系统卡住了很长时间,以至于新内容虽然从技术上说已经“进入”数据库,却有一段时间无法被搜索到。
在第一天,只有几千个 vector、单个节点时,这些问题一个都没有出现。等数据规模真正上来后,它们全都冒了出来。那一刻我才意识到:我一直把 Vector Database 当作存储系统来看待。但它不是。它是一个必须常驻内存、持续维护索引,并且每天 24 小时都能在毫秒级完成搜索的在线系统——而随着规模扩大,其中每一项要求都会消耗不同的资源。
Vector Database 不是一个用来存放 vector 的地方。它是一个需要全天候运行并常驻内存的系统——存储、索引、查询和更新,会以完全不同的方式消耗它的资源。
每个 chunk 的 embedding 只需创建一次。Vector Database 却必须永久保存其结果,并且无论自身增长到多大、底层数据变化得多频繁,都要立即对这些数据执行相似度搜索。这与 pipeline 前面任何阶段的成本都有本质区别——它不是一次性发生的,而是持续产生的。即使眼下没有任何人提问,系统也照样在运行。
第一个意外是:索引占用的空间比原始 vector 还要多。
原始 vector 只是一组数字——维度数量乘以每个数字占用的字节数。但在大规模场景下,大多数 Vector Database 并不会直接搜索原始 vector。为了提高搜索速度,它们会在 vector 之上构建索引结构,常见的有 HNSW 或 IVF。索引结构本身也会带来额外开销——图连接、聚类中心以及辅助管理信息——这些全都叠加在 vector 本身之上。
Raw vectors
│
▼
Index structure built on top (HNSW / IVF / etc.)
│
▼
Index overhead adds meaningfully more memory than the vectors alone
│
▼
Actual footprint is noticeably bigger than "vector count × dimension size" suggests
以下只是示例,并非实测数据:假设有 100 万个采用常见 embedding 维度的 vector,单纯作为原始数字存储时,可能占用几 GB;但加上索引开销后,同样的 100 万个 vector 在实际内存中完全可能占用多得多的空间。具体倍数完全取决于你选择的索引类型和配置。重点不在某个具体数字,而在于只用“vector 数量乘以维度大小”计算,永远都会低估真实占用。
磁盘存储很便宜,内存存储则不然——而为了让索引能够在毫秒级响应查询,其中的大部分内容都需要驻留在 RAM,而不是磁盘中。
Index needs to stay in memory for fast search
│
▼
More vectors → more RAM required to hold the index
│
▼
RAM is the most expensive resource per gigabyte in most infrastructure
│
▼
This cost exists 24/7, whether or not a single query is running right now
正是这项成本,让 Vector Database 给人的感觉不同于普通数据库。普通数据库可以把大部分数据保存在磁盘中,需要时再将相关内容调入内存。但如果一个 vector 索引为了完成相似度搜索,必须不断地在内存中换入换出,它也就不再快了——所以,你之所以愿意为 RAM 付费,就是为了避免这种情况。你付费并不是为了存储数据,而是为了让数据始终可以被立即访问。
向一个扁平列表中添加 vector 非常简单。将 vector 添加到 HNSW 图中,则意味着需要重新计算它相对于邻居应该处于什么位置——这是真实存在的 CPU 工作,并且每添加一个 vector 都要执行一次,而不是只在最后统一执行。
New batch of documents ingested
│
▼
Each new vector needs to be placed correctly in the index graph
│
▼
CPU-intensive, scales with both index size and batch size
│
▼
Large bulk ingests can visibly slow down or briefly block live queries
这就是开头所说“批量更新让系统卡住”的时刻。索引构建并不是存储附带产生的无关影响,而是一项主动且持续进行的计算。每次有新数据进入,都必须执行这项计算,而且它会与同时服务线上搜索流量的任务争夺相同的 CPU 和内存资源。
即使拥有优秀的索引,搜索所需的计算也不是免费的,并且其成本不会在数据增长时保持不变。
Small index
│
▼
Search touches a small neighborhood of the graph → fast, cheap
Large index (same query, much more data)
│
▼
Search has to traverse more of the graph to find the same quality of match
│
▼
More compute per query, potentially slower response, especially under concurrent load
近似索引存在的目的,正是避免搜索成本随着数据规模线性增长——但这句话中的“近似”二字承担了真正的代价。系统通常会提供一个参数,根据具体系统,它可能叫作 ef 或 nprobe,用于在搜索完整度与速度之间做取舍。为了速度把参数调低,也就意味着要牺牲一点检索准确率——这会悄悄转化成前几期提到的另一种成本:检索到的匹配结果稍差一点,回答稍微不完整一点,或者需要重试一次。
这其实就是成本 2 到成本 4 最终落到真实用户身上的感受。
More vectors + more concurrent queries
│
▼
More RAM pressure, more CPU contention, larger graph to traverse
│
▼
Response time per query creeps upward
│
▼
Users notice the assistant feels "slower" than it used to
出现这种情况并不需要系统发生故障,也不会有任何错误被抛出。系统只是在增长过程中逐渐变得更加沉重。除非有人持续观察查询延迟随时间的变化,否则这项成本就会堂而皇之地隐藏起来——直到终于有用户说,这个 bot 感觉变慢了。此时没有某个单一 bug 可以归咎,只有日积月累的规模压力。
如果整个索引只保存在一个节点上,那么只要发生一次硬件故障,整套 RAG 系统就会宕机。因此,生产系统会运行多个副本——也就是同一索引的多个副本实例。它们既可以并行处理查询,也可以在某个节点发生故障时接管服务。
1 node → 1× RAM cost, 1× CPU cost, no redundancy
3 nodes → 3× RAM cost, 3× CPU cost, real redundancy and more query throughput
这是一个非常直接的乘数,却很容易被低估,因为它感觉不像一项“新”成本——更像是同样的成本,只不过更安全了。但前面提到的成本 1 到成本 5,都会乘以你为了正常运行时间和查询吞吐量而决定部署的副本数量。
与第 3 期一样,一个小型计算示例比单纯用一段文字描述更容易让人直观感受到成本。这些数字只是示意,并非真实系统的实测结果——重点在于展示权衡关系,而不是具体数值。
Example
1 million vectors, single replica
│
▼
Raw vectors: a few GB
Index overhead on top: noticeably more
│
▼
Rough ballpark: somewhere in the low tens of GB of RAM to keep it fast
现在,再根据副本数量,以及一种常见的成本节省手段对它进行扩展:
1 replica → ~X GB RAM (baseline, no redundancy)
3 replicas → ~3× X GB RAM (same data, held three times, for uptime + throughput)
Apply quantization (e.g. compress vectors to lower precision)
│
▼
RAM footprint drops meaningfully — often by roughly half or more,
depending on the technique
│
▼
In exchange: a small, tunable dip in search accuracy
最后一步中,vector 的数量完全没有变化。唯一改变的只是压缩配置——它让 RAM 账单朝一个方向移动,也让准确率的旋钮朝另一个方向移动。这个例子概括了 Vector Database 的完整成本逻辑:你每拉动一个控制杆来降低资源账单,另一个控制杆——准确率、延迟或工程投入——就会悄悄朝相反方向移动。
添加新 vector 是一个问题,删除或更新旧 vector 则是另一个更加棘手的问题。这相当于 Vector Database 版本的第 3 期分块重做成本。
许多索引结构无法干净利落地处理删除操作。一个“已删除”的 vector 往往只是被标记为 tombstone,而不是真正从索引中移除。索引仍然需要携带它,在遍历时仍然可能触及它,直到定期执行的压缩或重建真正回收这部分空间。
Document gets updated or removed
│
▼
Old vector marked as deleted, not actually removed
│
▼
Index keeps carrying the dead weight
│
▼
Search quality and speed slowly degrade as tombstones accumulate
│
▼
Eventually requires a full index rebuild to actually clean up
│
▼
Rebuild is CPU-heavy and, depending on the system, can mean a period of reduced search quality or availability while it runs
这项成本会悄悄惩罚那些文档频繁变化的系统——政策会更新,产品会下架,价格会变动。现实世界中的每一次更新,都会在索引中留下一点最终必须偿还的痕迹。通常要等到某位工程师注意到搜索质量下降,并最终追查到某次从未执行的压缩任务时,问题才会暴露出来。
以上问题并不意味着 Vector Database 在大规模场景中无可救药,而是意味着必须主动管理成本,不能假设成本会自行消失。生产系统通常会依赖以下几种手段:
Quantization——以更低精度存储 vector,例如将 32-bit float 转换为 8-bit integer 或类似的压缩格式,从而大幅降低 RAM 使用量,代价则是搜索准确率会出现小幅且可调节的下降。
Tiered storage——将频繁访问或最近使用的 vector 保存在内存中,把更冷、很少被查询的 vector 下沉到磁盘,让 RAM 花在真正值得花费的地方。
Sharding——按照某种逻辑边界将索引拆分到多个节点上,使单个节点不必在内存中保存整个数据集。
Metadata pre-filtering——第 4 期介绍的方法在这里直接产生回报。在执行相似度搜索之前先按照 metadata 过滤,意味着搜索只需遍历索引中相关的子集,而不是整个索引。
Batched, off-peak reindexing——在流量较低的时间窗口执行索引重建和压缩,而不是在线实时执行,这样成本 7 中的清理工作就不会与真实用户的查询争夺资源。
Caching——对于真正重复出现的查询,可以完全跳过 vector 搜索,直接返回缓存结果。这是回答“如何降低 Vector Database 负载”时成本最低的方案。
这里的每一种手段都是真实的权衡,而不是免费的收益——Quantization 会牺牲一定的准确率,Tiered storage 会增加冷数据的访问延迟,Sharding 会增加工程复杂度。生产环境调优的目的不是消除这些成本,而是有意识地决定:你更愿意消耗哪一种资源。
与前两期的模式相同,Vector Database 会消耗:
RAM——最大的一项持续性成本,无论查询量多少,都需要全天候支付。
CPU——用于索引构建、重建和压缩。
Disk I/O——用于索引中采用分层存储或磁盘存储的部分。
Network——用于传输查询结果,以及在节点之间复制数据。
Latency——随着系统规模扩大,用户会直接感受到它。
Availability——当节点发生故障时,副本不足所带来的代价。
Search accuracy——每一种速度优化都会牺牲其中的一部分。
Engineering time——用于调整索引参数、规划重建索引的时间窗口,以及监控那些从不抛出错误的性能退化。
User trust——当“这个 bot 感觉很慢”成为人们开始公开讨论的问题时,用户信任就已经在悄悄流失。
本系列前面介绍的所有环节——数据提取、分块、metadata——对于每份文档来说,都只会发生一次,或者接近一次。Vector Database 是这个系列中第一个永远不会停止运行的阶段。它并不是一项只需在数据摄取时支付一次的成本,而是系统每运行一秒就要持续支付的成本,并且会随着新增的每一份文档,以及为了保持速度和可用性而增加的每一个副本不断增长。
Vector Database 收费的并不是 vector 本身,而是让这些 vector 始终可以被立即访问——而“始终可以立即访问”,是整个 pipeline 中最昂贵的承诺之一。
Vector Database 让我明白,仅仅维持数据可搜索,本身就是一件昂贵的事。但有一个问题,我一直在悄悄回避:即使拥有快速且调优良好的索引,我发送给 LLM 的真的是正确的 chunk,还是仅仅与查询最接近的那些 chunk?
为什么更少但选择更精准的 chunk,通常胜过更大、成本更高的模型。
减少噪声,采取行动。让我们深入探讨。
如需采取进一步措施,你可以考虑屏蔽此人和/或举报滥用行为。