1. 选型之前先搞清楚:向量数据库到底在解决什么类型的问题
很多人第一次接触向量数据库,是因为手头突然多了一堆非结构化数据——图片、音频、长文本、用户行为序列。传统数据库在这些数据面前基本是“睁眼瞎”,你没法用WHERE content LIKE '%某个意思%'去搜一段话的语义相似度。向量数据库的出现,本质上是把“语义相似”这件事变成了可计算、可索引、可查询的操作。
我先把结论放在前面:向量数据库不是普通数据库的替代品,它是一类针对高维向量近邻搜索做了极致优化的专用存储系统。普通数据库擅长的是精确匹配、范围查询、事务一致性、复杂关联;向量数据库擅长的是“给我找和这个向量最像的K个结果”。两者的设计目标从根上就不一样。
那为什么不能用普通数据库硬扛向量检索?假设你有1000万条768维的向量,用MySQL存成BLOB,每次查询都要全表扫描算余弦相似度。单次计算量是1000万次浮点运算,就算用上SIMD指令集,响应时间也在秒级甚至十秒级。而向量数据库通过HNSW、IVF、PQ这些索引结构,能把同样规模的检索压到毫秒级。这个差距不是靠加索引能弥补的,因为B树索引对高维向量的“距离排序”完全无能为力。
所以选型的第一步,不是比较产品参数,而是先判断你的业务里“相似性检索”到底占多大比重。如果只是偶尔跑个离线分析,普通数据库加个扩展就够了;如果是在线服务,QPS要求上百甚至上千,那专用向量数据库几乎是唯一选择。
1.1 从“精确匹配”到“近似最近邻”的思维转换
普通数据库的查询逻辑是“找到满足条件的记录”,条件是精确的、可枚举的。向量数据库的查询逻辑是“找到距离最近的记录”,距离是连续的、需要计算的。这个思维转换很关键,因为它直接决定了你对“召回率”和“延迟”的取舍。
近似最近邻(ANN)搜索的核心思想是:放弃100%的精确性,换取数量级的速度提升。比如HNSW通过构建多层图结构,在查询时只探索部分节点,就能以95%以上的召回率完成检索。这个95%不是缺陷,而是工程上的主动选择。你在选型时必须接受这个前提,否则就会陷入“为什么我的向量数据库搜出来的结果和暴力计算不完全一样”的困惑。
1.2 普通数据库加向量扩展:什么时候够用,什么时候不够
现在很多传统数据库都推出了向量扩展,比如PostgreSQL的pgvector、Elasticsearch的dense_vector、Redis的RediSearch。这些方案的好处是运维栈不变,数据不用搬家,事务和向量可以放在同一个查询里。对于数据量在百万级以下、QPS在几十以内的场景,pgvector完全够用,而且开发效率极高。
但一旦越过某个临界点,问题就会集中爆发。我实测过的一个案例:某推荐系统用pgvector存了800万条用户向量,查询延迟在并发50时还能维持在200ms左右,但并发上到200之后,P99直接飙到3秒以上。原因是pgvector的HNSW索引在内存中的图结构太大,频繁的随机内存访问导致CPU缓存命中率急剧下降。换成专用向量数据库后,同样的数据量和并发,P99稳定在80ms以内。
这个临界点没有绝对数值,但你可以用三个指标来判断:数据量是否超过500万条、QPS是否超过100、延迟要求是否低于100ms。三个里中两个,就该考虑专用方案了。
2. 拆开看两类数据库的底层架构差异
要做出靠谱的选型,不能只看benchmark数字,得理解它们内部是怎么干活的。普通数据库的存储引擎是为“行”或“列”设计的,而向量数据库的存储引擎是为“向量块”设计的。这个底层差异决定了它们在数据写入、索引构建、查询执行上的完全不同表现。
2.1 存储布局:行式、列式与向量块式
普通关系型数据库大多采用行式存储,一行数据的所有字段连续存放。这对事务处理很友好,但做向量检索时,你需要把每个向量的所有维度读出来算距离,行式存储会导致大量的随机I/O。列式数据库稍微好一点,但列式存储的压缩和编码方式是为标量设计的,对浮点向量的压缩效率并不理想。
向量数据库通常采用“向量块”存储:把一批向量的相同维度打包在一起,或者把整个向量连续存放并做对齐。这样在计算距离时,可以一次性加载多个向量到SIMD寄存器,并行计算。更关键的是,向量数据库会在内存中维护索引结构(如图、聚类中心、量化码本),这些结构的大小和访问模式都经过专门优化。
我打个比方:普通数据库像是一个按姓名排序的通讯录,你要找“名字听起来像张三的人”,只能一个个念出来对比;向量数据库像是一个按声音特征建好的索引,你哼一段旋律,它就能直接跳到最像的那几页。
2.2 索引结构:B+树、倒排索引与HNSW/IVF的适用边界
B+树索引解决的是“一维有序数据的范围查找”,倒排索引解决的是“关键词到文档的映射”。这两个结构在高维空间里都会失效,因为高维空间的“距离”没有全序关系,你没法像排序整数那样排序向量。
向量数据库用的HNSW(分层可导航小世界图)本质上是构建了一个多层的近邻图。查询时从顶层开始,每层贪心地向目标靠近,最后在底层做精细搜索。IVF(倒排文件)则是先用聚类把向量空间划分成多个桶,查询时只搜索距离最近的几个桶。PQ(乘积量化)进一步把向量压缩成短码,用查表代替浮点计算。
这三种结构的组合决定了向量数据库的性能特征。HNSW召回率高但内存占用大,IVF内存友好但需要训练,PQ压缩率高但精度损失明显。选型时要看你的场景更在意哪个:是内存成本、查询速度,还是召回精度。
2.3 查询执行:事务型负载与分析型负载的冲突
普通数据库的查询执行器是为事务型负载优化的:短查询、高并发、强一致性。向量检索是典型的分析型负载:单次查询计算量大、需要扫描大量候选、对一致性要求不高。把这两种负载混在同一个实例里,就会出现资源争抢。
我见过一个典型的翻车案例:某团队在PostgreSQL里同时跑业务事务和向量检索,白天业务高峰时向量查询把shared_buffers占满,导致业务查询频繁读磁盘;晚上跑向量批量任务时,又因为长事务阻塞了业务写入。最后不得不把向量数据拆到独立实例,用逻辑复制同步。这个教训说明:即使你决定用普通数据库加扩展,也强烈建议把向量负载和事务负载做物理隔离。
3. 选型决策树:从业务场景倒推技术方案
选型最怕的就是“拿着锤子找钉子”。正确的做法是从业务场景出发,先明确数据特征、查询模式、规模预期,再倒推技术方案。我整理了一个决策流程,你可以直接对照自己的项目走一遍。
3.1 第一步:判断数据规模和增长曲线
数据规模是选型的第一道分水岭。这里说的规模不只是当前条数,还包括增长速度和向量的维度。
| 数据规模 | 向量维度 | 推荐方案 | 理由 |
|---|---|---|---|
| < 100万 | < 512 | 普通数据库+向量扩展 | 运维简单,开发快,性能可接受 |
| 100万-500万 | 512-1024 | 普通数据库+向量扩展或轻量专用库 | 看QPS和延迟要求 |
| 500万-5000万 | 768-1536 | 专用向量数据库 | 需要专业索引和内存管理 |
| > 5000万 | 任意 | 分布式向量数据库 | 单机内存和算力已到瓶颈 |
这个表不是绝对的,但可以作为起点。关键是要估算内存需求:HNSW索引的内存占用大约是原始向量数据的1.5到2倍。1000万条768维浮点向量,原始数据约30GB,索引后可能到60GB。如果你的服务器内存只有64GB,那就很紧张了。
3.2 第二步:明确查询模式与延迟预算
查询模式决定了你需要什么样的索引和过滤能力。常见的查询模式有几种:
- 纯向量检索:只给一个查询向量,返回最相似的K个结果。这是最简单的场景,几乎所有向量数据库都能做好。
- 向量+标量过滤:在向量检索的同时,还要满足一些标量条件,比如“找相似图片,但必须是最近一周上传的”。这个场景对数据库的过滤能力要求很高,很多向量数据库的过滤是在检索后做的,会导致召回率下降。
- 多向量联合查询:比如同时用文本向量和图像向量做多模态检索。这需要数据库支持多向量字段和跨字段的联合评分。
- 批量检索:一次给几百上千个查询向量,要求批量返回结果。这对吞吐量要求高,但对单次延迟不敏感。
延迟预算也很关键。在线服务通常要求P99在100ms以内,离线分析可以接受秒级。如果你的延迟预算很紧,就必须选择支持内存索引的方案,并且要预留足够的CPU核数做并行计算。
3.3 第三步:评估过滤与事务需求
过滤需求是选型中最容易被低估的因素。很多团队选了一个纯向量数据库,上线后才发现业务需要复杂的标量过滤,结果只能把过滤逻辑放到应用层,先检索大量候选再过滤,导致延迟和成本都失控。
如果你需要强事务保证,比如向量数据和业务数据必须同时写入、同时可见,那普通数据库加扩展是更自然的选择。专用向量数据库大多只提供最终一致性或单文档事务,跨文档事务支持较弱。
我个人的经验是:如果过滤条件的选择性很高(比如能过滤掉90%以上的数据),优先考虑支持原生过滤的向量数据库;如果过滤条件很宽泛,或者事务要求强,优先考虑普通数据库加扩展。
3.4 第四步:运维成本与团队技术栈匹配度
这一条经常被忽略,但实际影响很大。引入一个专用向量数据库,意味着你要多维护一套集群、多一套监控、多一套备份恢复流程。如果团队规模不大,运维负担会很明显。
普通数据库加扩展的好处是复用现有运维体系。你不需要额外学习一套新的部署和调优方法,备份、监控、告警都是现成的。对于中小团队来说,这个优势有时候比性能差距更重要。
但专用向量数据库在性能调优上的空间更大。它们通常提供了丰富的参数,比如HNSW的efConstruction、M、efSearch,IVF的nlist、nprobe。你可以根据数据分布和查询模式精细调整。普通数据库的向量扩展通常只暴露少量参数,调优余地有限。
4. 实测对比:几个典型场景下的性能表现
光讲理论不够,我拿几个实际跑过的场景来说明。这些测试都在同一批硬件上完成,数据是模拟生成的,但分布特征贴近真实业务。
4.1 场景一:百万级文本语义检索
数据量100万条,向量维度768,查询是给一段话找最相似的10条。测试了三种方案:PostgreSQL+pgvector、Elasticsearch dense_vector、专用向量数据库A。
| 方案 | 索引构建时间 | 平均延迟 | P99延迟 | 召回率@10 |
|---|---|---|---|---|
| pgvector | 12分钟 | 45ms | 120ms | 0.97 |
| ES dense_vector | 8分钟 | 38ms | 95ms | 0.95 |
| 专用库A | 6分钟 | 12ms | 35ms | 0.99 |
这个规模下,三者都能用。pgvector的延迟稍高,但考虑到它省去了独立集群的运维,性价比很高。专用库A的优势在P99和召回率,适合对稳定性要求极高的在线服务。
4.2 场景二:千万级图像特征检索
数据量2000万条,向量维度512,查询是给一张图找最相似的50张。这个规模下pgvector开始吃力,ES的延迟也明显上升。
| 方案 | 内存占用 | 平均延迟 | P99延迟 | 召回率@50 |
|---|---|---|---|---|
| pgvector | 48GB | 380ms | 1.2s | 0.92 |
| ES dense_vector | 52GB | 290ms | 890ms | 0.90 |
| 专用库A | 40GB | 28ms | 75ms | 0.98 |
| 专用库B | 35GB | 22ms | 60ms | 0.97 |
差距非常明显。专用库的内存占用反而更低,因为它们用了更激进的量化压缩。延迟差距在10倍以上,这个规模下普通数据库加扩展已经不适合在线服务了。
4.3 场景三:带标量过滤的混合查询
数据量500万条,向量维度256,查询是“找相似商品,但价格必须在100-500之间,且库存大于0”。这个场景考验的是过滤能力。
| 方案 | 过滤方式 | 平均延迟 | 召回率@20 |
|---|---|---|---|
| pgvector | 先向量后过滤 | 85ms | 0.72 |
| 专用库A | 原生过滤 | 18ms | 0.96 |
| 专用库B | 原生过滤 | 15ms | 0.95 |
pgvector的召回率暴跌,因为它先检索了K个结果,再过滤掉不满足条件的,导致实际返回数量不足。专用库的原生过滤能在检索过程中就排除不满足条件的向量,召回率和延迟都更好。
这个测试说明:如果你的业务有强过滤需求,选型时一定要把过滤能力作为核心指标,而不是只看纯向量检索的性能。
5. 那些选型文档不会告诉你的踩坑经验
理论分析和benchmark只能覆盖一部分问题,真正上线后遇到的坑往往更隐蔽。我整理了几个实际踩过的坑,希望能帮你少走弯路。
5.1 索引参数调优的“甜蜜点”陷阱
HNSW的M参数控制每个节点的连接数,M越大召回率越高,但内存占用和构建时间也越大。很多文档会建议M=16或M=32,但实际最优值取决于你的数据分布。
我踩过的坑是:在一个聚类特征明显的商品向量集上,用M=32构建索引后,查询延迟比M=16高了40%,但召回率只提升了0.5%。原因是数据本身聚类紧密,每个节点不需要太多连接就能找到近邻。后来降到M=12,召回率几乎不变,延迟降了30%。
经验法则:先用小规模数据做参数扫描,画出召回率-延迟曲线,找到拐点。不要盲目套用默认值。
5.2 向量归一化与距离度量的隐性错误
余弦相似度和内积在向量归一化后是等价的,但很多数据库对这两者的处理方式不同。如果你存的是未归一化的向量,却用余弦距离查询,数据库会在每次查询时做归一化,增加额外开销。更糟糕的是,有些数据库的索引构建和查询使用了不同的距离度量,导致召回率异常低。
我遇到过一个案例:团队用内积索引构建,但查询时传了余弦距离参数,结果返回的结果完全不对。排查了半天才发现是度量不一致。建议在数据入库前统一做L2归一化,然后明确使用内积或余弦,并在索引和查询时保持一致。
5.3 批量写入时的内存爆炸
向量数据库的索引构建通常是内存密集型的。如果你一次性批量写入大量向量,索引构建过程可能耗尽内存。我见过一个团队一次性导入500万条向量,直接把32GB内存的实例打爆了。
正确的做法是分批写入,每批控制在1万到5万条,并在批次之间留出索引合并的时间。有些数据库提供了“延迟索引构建”的选项,可以先写入再统一建索引,但这样查询会走暴力扫描,延迟很高。建议在业务低峰期做批量导入,并监控内存使用曲线。
5.4 删除和更新带来的索引碎片
向量数据库的删除通常不是物理删除,而是标记删除。被标记的向量仍然占用索引空间,直到触发索引重建。如果你频繁更新向量,索引会迅速膨胀,查询性能下降。
我实测过一个场景:每天更新5%的向量,一周后索引大小增加了30%,查询延迟上升了50%。解决办法是定期做索引重建,或者选择支持增量索引维护的数据库。如果你的业务有高频更新需求,选型时一定要问清楚删除和更新的处理机制。
6. 混合架构:把两类数据库放在该放的位置
聊了这么多对比,其实最务实的方案往往是混合架构:用普通数据库存业务数据和标量字段,用向量数据库存向量和索引,两者通过主键关联。这样各取所长,既保证了事务一致性,又获得了向量检索的性能。
6.1 数据同步与一致性保障
混合架构的核心挑战是数据同步。业务数据写入普通数据库后,需要把对应的向量写入向量数据库。这个同步过程可以是同步的,也可以是异步的。
同步写入的优点是强一致,缺点是延迟高,因为要等两个数据库都确认。异步写入的优点是快,但可能出现业务数据已更新、向量数据还是旧的情况。对于推荐系统这类场景,短暂的不一致通常可以接受;对于搜索场景,可能需要更严格的同步机制。
我常用的方案是:业务数据写入普通数据库,同时发一条消息到队列,由消费者异步写入向量数据库。如果对一致性要求高,可以在向量数据库里存一个版本号,查询时校验版本,不一致就回源。
6.2 查询路由与结果合并
混合架构下,查询需要路由到正确的数据库。纯标量查询走普通数据库,纯向量查询走向量数据库,混合查询则需要拆分:先在普通数据库做标量过滤拿到候选ID,再在向量数据库里做向量检索并过滤ID。
这个拆分逻辑听起来简单,但实际实现时要注意候选集大小。如果标量过滤后的候选集有几十万条,把这些ID传给向量数据库做过滤,性能会很差。更好的做法是把标量过滤条件下推到向量数据库,让它原生支持过滤。
6.3 成本与复杂度的平衡点
混合架构的运维复杂度比单库高,但成本可能更低。普通数据库的存储成本远低于向量数据库的内存成本。把标量数据和向量数据分开存储,可以避免为标量数据支付高昂的内存费用。
我算过一笔账:1000万条数据,如果全放在向量数据库,需要约60GB内存,月成本可能在数千元;如果标量数据放普通数据库,向量数据放向量数据库,向量数据库只需要存向量和ID,内存需求降到40GB左右,成本降低三分之一。
这个平衡点取决于你的数据特征和成本敏感度。对于大多数中等规模的应用,混合架构是性价比最高的选择。
7. 面向未来的选型:别只看今天的数据量
选型不仅要解决当前问题,还要为未来留出空间。我见过太多团队因为初期选型过于保守,半年后就被迫迁移,付出的代价远大于当初直接选一个稍重的方案。
7.1 预留扩展性的几个关键指标
判断一个方案是否有扩展性,可以看几个指标:是否支持分布式部署、是否支持在线扩缩容、是否支持多副本、是否支持冷热数据分层。这些能力在数据量增长时会变得至关重要。
专用向量数据库通常在这些方面做得更好。它们从设计之初就考虑了分布式和扩展性。普通数据库加扩展的方案,扩展性受限于底层数据库的能力,比如pgvector的分布式需要依赖Citus等扩展,复杂度较高。
7.2 从单机到分布式的迁移成本
迁移成本不只是数据搬迁的时间,还包括索引重建、查询适配、性能调优。如果初期选了一个不支持分布式的方案,后期迁移可能需要停机数小时甚至数天。
我的建议是:如果业务预期在一年内数据量会增长5倍以上,直接选择支持分布式的方案。即使初期只用单机部署,也能在需要时平滑扩展。
7.3 什么时候该果断放弃普通数据库
普通数据库加扩展在数据量超过500万、QPS超过100、延迟要求低于100ms这三个条件中满足两个时,就应该认真考虑专用方案了。继续硬扛的代价是开发人员不断调优、运维人员不断救火、用户体验不断下降。
我自己的判断标准更简单:如果向量检索的延迟已经成为业务增长的瓶颈,或者团队每周都要花时间处理向量相关的性能问题,那就该换了。技术选型是为业务服务的,当维护成本超过迁移成本时,迁移就是理性的选择。
7.4 一个实用的选型检查清单
最后给你一个可以直接用的检查清单,在选型时逐条确认:
- 数据量当前多少?一年后预计多少?
- 向量维度多少?是否可能增加?
- QPS峰值多少?平均多少?
- 延迟要求P99多少?
- 是否需要标量过滤?过滤条件的选择性如何?
- 是否需要事务保证?
- 团队是否有运维分布式系统的经验?
- 预算能覆盖多大的内存和CPU?
- 是否需要多租户隔离?
- 是否有数据备份和恢复的要求?
把这十个问题回答清楚,选型基本就不会跑偏。向量数据库和普通数据库不是非此即彼的关系,而是各司其职的工具。理解它们的能力边界,根据业务场景做组合,才是工程上最务实的做法。