
上个月帮朋友排查一个RAG项目的检索漏数据问题最后结论出在“双写”上业务数据写在业务库向量写在独立的向量数据库里两边的更新不在同一个事务里同步一旦失败就会漏数据。我给他的建议很直接——如果数据量在千万级以下业务数据和向量数据又强关联不如把向量检索放回业务数据库本身去。当时他用的OceanBase 4.3已经能在SQL里直接建向量索引了底层算法库就是VSAG。VSAG这个名字很多人第一次看到会懵它不是独立的数据库也不是网上那些开源ANN库的套壳而是OceanBase自研的向量近似最近邻检索算法库。这篇文章不打算写成源码解析我更想从一个实际用户的视角把VSAG到底解决了什么问题、索引是怎么设计的、建索引时那些参数到底是什么含义、以及我踩过的几个坑讲清楚。适合正在做RAG、以图搜图、相似推荐又不想在业务系统旁边再维护一套向量数据库的团队参考。1. 数据库场景下为什么现成的ANN库不够用1.1 现成ANN库的“水土不服”集中在哪很多团队第一次接触向量检索都会先试Faiss、HNSW这类开源算法库。单看算法指标它们在百万级、千万级数据集上确实能跑出很漂亮的召回率和延迟但一旦要嵌到业务系统里问题就接踵而来。第一个问题是持久化和事务。Faiss本质上是一套内存态算法库索引对象在内存里需要自己负责序列化、落盘、加载进程一挂索引就丢了。业务库里的数据是跟事务走的你往表里插了一行向量索引也得跟着变化这个变化在Faiss里没有任何事务语义。两个系统之间只能靠应用层代码去同步一旦同步顺序搞错或者中途失败索引和数据就对不上了。第二个问题是访问模式。HNSW这类图算法在搜索阶段是典型的随机访问它沿着图的边在内存里跳来跳去这个模式在纯内存数据库、独立向量数据库里没问题但和共享存储、共享缓冲池的架构结合在一起性能就很容易被打折扣。还有并发写入时的锁竞争HNSW原生的插入逻辑并不是为数据库这种高频DML场景设计的。第三个问题是运维成本。独立部署一套向量检索服务意味着要额外监控一个进程要考虑它的高可用、扩容、备份恢复还要实现业务库到向量库的同步管道。数据量不大时这套东西带来的复杂度远比它解决的问题多。1.2 VSAG的定位不是“向量数据库”而是数据库里的索引算法VSAG和Faiss、HNSW最大的区别在于它生来就是给数据库用的。它不是独立服务而是OceanBase存储引擎里的一种索引结构和B树索引、全文索引平级。你在SQL里写一句CREATE VECTOR INDEX系统就在后台帮你把向量列组织成VSAG索引查询的时候优化器自动决定走不走这个索引。这样的设计让几个麻烦事自动消失了。首先向量索引和数据在同一个存储引擎里插入、更新、删除都跟着事务走根本不存在双写一致性问题事务提交了索引就是新的事务回滚了索引也跟着回滚。其次查询的时候可以把向量相似度检索和普通SQL条件混在一个语句里比如“找和这张图片最像的前10张但限定类目是女装”这在纯向量数据库里往往要做两次查询再过滤而在OceanBase里就是一条SQL。我理解VSAG的研发动机也很简单与其让用户在外面拼一套复杂的向量检索链路不如把向量检索做成数据库的内置能力让SQL把向量条件和其他条件一起处理。这在数据量处于百万到千万级、又和业务数据强关联的场景里架构会清爽非常多。1.3 一次小规模对比VSAG和外部向量数据库的差距在哪我自己在测试环境做过一轮对比数据是200万条768维的float向量同时每个向量带类目、来源、创建时间三个标量字段。一边是把向量导入一个独立的向量数据库业务字段留在OceanBase查询时先向量检索出ID再回表过滤另一边是用OceanBase的VSAG索引一条SQL同时带向量排序和标量过滤。结论说直白一点纯向量查询的延迟两边差不多但一旦加标量过滤条件VSAG这边的优势就出来了因为它可以在索引扫描阶段就考虑过滤条件避免把大量根本不符合条件的候选向量捞回来再过滤。外部向量数据库在这种场景下要么全量向量查完再过滤要么依赖它自己的标量过滤能力但这个能力往往比较弱复合条件一多就捉襟见肘。当然独立向量数据库在海量数据、超低延迟、多租户隔离这些方面有它的优势这取决于场景。但如果你是OceanBase用户业务数据已经在这里了VSAG是优先级高得多的选择因为它省掉了整个同步管道。2. VSAG的分段式索引结构倒排粗选、图精排、量化压缩2.1 第一段检索倒排结构如何快速锁定候选集如果你去看VSAG的实现思路会发现它不是一个全新的“天外来客”而是把向量检索领域积累下来的成熟手段组合起来针对数据库场景做了改造。整个索引大致分成两段先粗选再精排。这个思路和IVF倒排文件一脉相承。倒排结构做的事情很朴素把所有向量做一次聚类比如聚成N个桶每个桶有一个聚类中心向量。索引构建时每个向量被归入离它最近的桶查询时先算一下查询向量和N个聚类中心的距离选出最近的若干个桶然后只在候选桶内部搜索。类比到找书图书馆按大类分好了书架你要找机器学习相关的书肯定不会从文学区开始逐本翻先定位到计算机类书架再在小范围内找。粗选阶段的核心目标不是精确而是高召回。宁可多捞一些不相关的候选也不能漏掉真正近邻的向量。VSAG在粗选阶段会刻意把候选集放大比如选8到16个桶桶内的向量再结合后续的精确搜索来兜底。2.2 第二段检索近邻图在候选集内做精细搜索粗选把搜索空间从全量向量缩小到几百甚至几十个候选之后第二段再在候选集上做精细搜索。这里VSAG引入了图结构构建方式类似HNSW把桶内的代表性向量作为图的节点节点之间按照距离关系连边搜索时从一个入口节点出发沿着邻居节点不断贪心前进每次只走“比当前节点更近”的邻居直到局部收敛。图搜索的好处是它不需要遍历候选集里的每一个向量而是通过图的结构性跳转快速逼近真正的近邻。复杂度近似对数级别数据量翻几倍搜索时间并不会线性增长。这跟人找人差不多你不认识圈子里所有人但你认识几个关键节点顺着这些节点介绍的路径很快就能找到目标。VSAG在图的构建上做了一些针对数据库的适配比如对内存占用有更严格的配额控制分段构建来降低对事务日志和WAL的压力以及在图的边数上做了权衡避免索引体积膨胀过快。这些细节在纯算法库里不会有人替你考虑但进了数据库就绕不开。2.3 向量压缩PQ量化是怎么把内存占用压下来的200万条768维float向量光原始数据就要吃掉差不多6GB内存到了亿级就变成几百GB纯内存索引在绝大多数业务场景里都撑不住。VSAG解决这个问题的手段是量化压缩最核心的是PQ乘积量化。PQ的思路是把高维向量切成若干低维子段每个子段单独做聚类然后用“离哪个聚类中心最近”的码字ID来替代该子段的原始数值。比如768维向量切成64段每段12维每段用256个聚类中心做量化那每段只需要1个字节来表示整个向量就被压缩成64字节内存占用骤降。压缩必然带来精度损失所以VSAG的策略不是全链路都用压缩后的向量算距离而是压缩向量用于粗选阶段的快速比对精排阶段对少量候选再回调原始向量重算距离。这样既享受了压缩带来的内存红利又在最终结果上保持了精度。2.4 两段式设计给数据库查询带来的额外好处我之前看一个文档里提到VSAG索引和普通HNSW索引的一个区别HNSW是单层图结构直接全量搜索VSAG是“倒排图”的组合结构。这个设计在数据库场景里有几个实际好处。第一个好处是过滤条件下更好优化。当你带着WHERE类目女装的条件查询时系统可以先利用倒排结构定位到相关桶再在桶内判断过滤条件而不是全图搜索完了再过滤。这个顺序差别在数据量大时是数量级的性能差异。第二个好处是受数据分布影响小。HNSW在数据分布不均匀时容易产生“断边”现象某些区域搜索要绕很长的路。倒排结构相当于先把空间切碎每个区域独立构图单区域的图规模小搜索路径也更可控。第三个好处是可解释性好。VSAG暴露出来的max_scan_ratio参数本质上就是在控制“最多扫描多少比例的向量”这个比例可以直接换算成最坏的查询耗时对DBA做容量规划和限流都更友好。3. 建索引的实操路径SQL写法与关键参数语义3.1 一个能跑的建索引示例以我当时用的OceanBase版本为例建表时定义向量列之后创建向量索引的SQL大致如下细节以你使用的版本官方文档为准但整体结构差别不大-- 建表embedding列存向量 CREATE TABLE doc_vectors ( id BIGINT PRIMARY KEY, content TEXT, category VARCHAR(64), embedding FLOAT[] -- 向量列 ); -- 建 VSAG 向量索引 CREATE VECTOR INDEX idx_doc_vec ON doc_vectors(embedding) WITH ( distance_type IP, type VSAG, max_scan_ratio 0.02, pq_dimensional_size 64 );查询的时候直接在SQL里用向量距离函数SELECT id, VEC_DISTANCE(embedding, :query_embedding, IP) AS dist FROM doc_vectors ORDER BY dist LIMIT 10;如果带过滤条件直接在后面加WHERE就行SELECT id, VEC_DISTANCE(embedding, :query_embedding, IP) AS dist FROM doc_vectors WHERE category 女装 ORDER BY dist LIMIT 10;这在外部分离式架构里是最麻烦的复合查询在OceanBase里就是很自然的SQL写法。3.2 核心参数速查与推荐配置参数含义如果不捋清楚后面调优很容易抓瞎。我根据自己的使用经验做了一张速查表参数作用经验取值type索引类型AUTO会由系统自动选择一般用AUTO明确要VSAG时直接写VSAGdistance_type距离函数IP内积、L2欧氏距离、CSINE余弦文本embedding常用IP或CSINE图像特征常用L2max_scan_ratio最大扫描比例控制候选集规模上限默认0.05左右有强过滤条件时建议调大到0.1以上pq_dimensional_sizePQ压缩的子段维度大小一般取64向量维度低于256时建议减小到32或16maintenance_memory_limit索引构建时可用的内存上限按实例内存配通常给2GB到8GB先说distance_type的选择。内积和余弦在一些归一化场景下是等价的因为向量模长固定时内积大小就是余弦相似度的大小。但如果embedding没有归一化内积会偏向模长大的向量这时候要么先归一化要么用余弦距离。我的习惯是文本类embedding统一归一化后走IP速度更快图像、多模态特征直接用L2。pq_dimensional_size直接影响内存占用和精度。子段越粗比如到128压缩率越高但精度损失也越大子段越细越接近原始向量但压缩优势就没了。768维向量我用64是经过实际对比的精度损失在可接受范围内内存压掉了80%以上。3.3 参数背后的权衡逻辑召回率、延迟、内存很多人在第一次调VSAG参数时都会问到底怎么调才能既快又准答案是想清楚你的业务优先级。如果你要的是召回率优先比如法律文档检索、代码搜索漏掉一个关键结果可能代价很大那就应该把max_scan_ratio调大让候选集更大同时把pq_dimensional_size调小一点减少量化损失。代价是查询延迟变高内存占用变大。如果你要的是延迟优先比如实时推荐、在线搜索每多1毫秒都影响用户体验那就反过来把max_scan_ratio压到0.01甚至更低用粗一点的PQ。代价是长尾结果可能不那么准。如果你要的是内存优先比如实例内存有限、不想为了向量索引单独扩内存那PQ子段可以设到128同时减少图的边数有些版本支持相关参数。代价是精度出现一定下降。这里没有“最优参数”只有“最适合你业务的参数”。我建议上线前用线上真实数据做一次召回率和延迟的对比测试至少跑一组max_scan_ratio从0.01到0.1的曲线找到拐点再确定配置。3.4 AUTO参数到底在帮你决定什么OceanBase在索引类型里提供了AUTO选项。AUTO模式下系统会根据数据量、数据分布、可用内存自动选择走HNSW、IVFFLAT还是VSAG。我刚接触的时候觉得既然能自动选那我为什么不永远用AUTO实际跑了一段时间我的结论是AUTO适合你不清楚该选什么索引的场景但如果你想稳定复现性能最好还是显式指定VSAG。原因在于AUTO的自动选择是根据构建时刻的数据分布估算的数据量在增长、分布发生变化之后最初的“最优选择”未必仍然最优。而显式指定VSAG配合固定参数线上行为是可预期的出了问题也能根据参数快速回溯。AUTO真正有价值的地方在于它可以作为一个“基准档位”。你拿AUTO建出来的索引性能当基线再去手动调整参数对比能少走很多弯路。4. 三种典型查询场景下的实测表现与架构红利4.1 纯向量检索以图搜图与相似推荐先看最简单的场景没有过滤条件就是拿一个查询向量去库里找最相似的K个向量。我测试环境里200万条768维向量归一化后的IP距离VSAG索引max_scan_ratio设为0.02单条查询平均耗时在8到15毫秒p99能压到40毫秒以内top10结果的召回率对比暴力扫描在95%以上。这个表现和独立部署的向量数据库基本处于同一水平。但有一个细节值得注意数据量增长时VSAG的延迟曲线比纯HNSW更平稳。原因还是前面说的分段式结构倒排桶把搜索空间限制在局部数据量翻倍时每个桶内的数据量增长远低于整体增长所以延迟不会跟着线性涨。如果你做的是以图搜图、相似商品推荐这类纯向量场景VSAG完全够用不需要为了这个能力单独引入外部组件。4.2 向量与标量混合过滤RAG和高召回场景的刚需真正让VSAG和外部向量数据库拉开差距的是带标量过滤条件的复合查询。做过RAG的人都知道一个知识库往往要按文档来源、时间范围、权限分组做过滤不然检索结果会出现“语义相似但来源不允许”的内容。我实测过一个场景500万条资料按部门权限过滤一个用户只能看10%的数据。用外部向量数据库的典型做法是先查出该用户有权限的文档ID列表再把列表作为过滤条件去向量库里做检索数据量大时这个ID列表本身就有几万到几十万条传递条件、过滤、再排序整个链路会到几百毫秒甚至秒级。VSAG的做法是在索引扫描阶段就把过滤条件下推到倒排桶内部先缩小到用户有权限的桶范围再在桶内做向量精排两条SQL合一条整体延迟控制在50毫秒左右。这种差别在用户量大、权限规则复杂的系统里是体感级别的差距。这里有一个操作经验带过滤条件时max_scan_ratio一定要比对纯向量查询设置的更大。因为过滤后候选集本身变小如果扫描比例上限还设得和全量查询一样可能根本搜不到足够多的候选导致漏检。4.3 写入密集时索引维护的压力与对策向量索引不是免费的午餐写入越频繁索引维护开销越大。VSAG在插入、更新、删除时需要同步维护倒排桶和桶内图结构。我观察到的现象是批量导入阶段比如初始化知识库索引构建速度明显慢于纯数据写入主要体现在CPU和内存的占用率上。我的建议是分阶段操作先批量导数据导完再建索引。如果数据是持续小批量进来的可以设置一个较低的maintenance_memory_limit避免索引构建挤占业务查询的资源。还有一个小技巧是如果业务允许可以定期重建索引。数据分布变了、反复增删之后索引内部会产生一些“脏桶”或者图结构退化重建能恢复查询性能。我一般是线上跑一两周就安排一次低峰期重建。4.4 什么时候需要外部向量数据库什么时候不需要有些团队看了VSAG觉得好又看Milvus、Qdrant的基准测试觉得香纠结要不要拆。我的判断标准很简单看数据是否和业务强关联。如果向量数据是你业务表的一部分比如商品描述、文档内容、工单记录加了向量索引后还要频繁和业务字段联查那闭眼用VSAG外部向量数据库只会给你增加同步负担。如果向量数据是独立的海量特征库比如数亿级别的用户行为向量、图片特征库业务字段基本不参与过滤那你用独立的向量数据库更合适它对超大索引的分布式扩展、多副本容灾支持得更完善。5. 我在生产环境踩过的几个坑以及对应的调优思路5.1 PQ压缩维度设置不当召回率长尾崩塌第一次用VSAG的时候我把pq_dimensional_size直接设成了128想最大化压缩率内存确实省了但上线之后发现一个严重问题整体召回率看着还行但长尾部分出了问题。具体来说是某些相似度本应很高的查询返回结果里出现了完全不相关的项。后来排查的链路是这样的先拿同一批查询对比暴力扫描的结果发现召回率从95%掉到了86%左右差距集中在那些查询向量本身处于聚类边界附近的case上。这种向量本来离哪个聚类中心都有点远粗选阶段靠的是量化向量计算的近似距离PQ子段太粗时近似距离的排序和真实距离的排序偏差被放大真正近邻的桶反而没被选进候选集。后来我把pq_dimensional_size调回64召回率恢复到94%以上。这个坑的关键教训是PQ压缩维度不能只看内存收益还要看查询向量的分布。我后来习惯在建索引前先抽样做一次分布分析如果查询向量经常落在聚类边界PQ子段就设细一点。5.2 max_scan_ratio没配合过滤条件结果被“漏检”另一个印象深刻的坑是在带过滤条件时出现了检索结果数量明显偏少。当时有个运营反馈说某个类目下相似产品搜不全明明库里这个类目有几千条数据返回的相似结果只有几十条。排查过程比较长一开始怀疑向量本身问题重新做了embedding也没有改善。后来我对比了带过滤和不带过滤的查询发现不带过滤时一切正常带过滤后结果数暴跌于是把怀疑点放到了索引参数上。翻看建索引的DDLmax_scan_ratio写的是0.01全量查询这个值够用但加上类目过滤之后倒排桶内参与精排的候选向量首先要满足类目条件实际能通过过滤的候选数量远小于理论值排完序后返回的topK自然就“营养不良”了。我当时是先把max_scan_ratio从0.01调到0.05结果数量恢复正常再到0.02左右找到了性能和数据量的平衡点。这个坑的通用教训是过滤条件下max_scan_ratio要按“过滤后数据占比”的倒数去放大。比如一个类目只占全量数据的10%那你至少要把扫描比例放大到全量查询的10倍才能保证同样的候选规模。5.3 大批量导数据时索引构建的节奏控制还有一次是我把历史数据从旧系统迁移过来一次性要导3000万条带向量数据。刚开始图省事建好表和数据索引后直接跑导入结果发现索引构建把CPU吃满业务查询延迟飙升。后来我调整了策略先建表不建向量索引只做纯数据导入导入完成后再执行CREATE VECTOR INDEX同时设置maintenance_memory_limit限制构建内存让索引构建在后台慢慢跑不抢占业务查询资源。整个流程走下来导入时间没怎么增加业务查询基本没受影响。我发现很多人会忽略维护内存上限这个参数但它真的很有用尤其在混合负载场景里它决定了索引构建能不能“见缝插针”地跑。如果你觉得线上波动比较大可以先调低这个值低峰期再放开。5.4 一个被验证有效的思路用VSAG做聚类中心的近邻搜索最后分享一个比较巧妙的用法是我在做一个推荐系统的粗排模块时摸索出来的。当时有几十万个聚类中心每个用户要快速找到最近的几百个聚类中心如果逐个算距离开销不小。后来我直接把聚类中心也建成一张VSAG索引表用向量检索去查“离用户最近的聚类中心”查一次只需要几毫秒。这个思路的本质是把“计算最近聚类”转换成“向量检索最近邻”利用VSAG的索引加速省掉了大量重复计算。类似的做法还可以用于关键词聚类、异常检测里的中心点匹配。只要你能把问题抽象成“找一个向量最近的K个向量”VSAG就能派上用场不必拘泥于传统的数据库检索场景。最后再分享一个个人经验向量索引的参数调优不要指望一劳永逸。数据分布会变业务查询模式会变上线之后至少要定期观察召回率和延迟的走势。VSAG的优势在于它给了你足够明确的参数旋钮你能清楚地知道该转哪个、往哪个方向转。这比黑盒的向量数据库要可控得多也是我现在更倾向于把向量检索收敛到业务数据库里的一个重要原因。