最近在搞一个MCP服务,给内部工具接了个向量数据库(用的Milvus),想通过MCP工具暴露语义检索能力。但发现一个问题:我往collection里灌了几万条数据,embedding也存了,索引也建了(用的HNSW),但每次通过MCP调用查询接口时,响应时间特别慢,日志显示走的还是全量扫描,根本没有命中索引。
MCP里调向量数据库做RAG,结果每次查询都全量扫描,快崩了
全部回复
共 27 条Milvus的HNSW索引没生效,大概率是查询时指定的metric type和建索引时不一致,或者filter字段没走索引导致fallback到暴力扫描。我之前也踩过这个坑,建议先查下query的params里有没有带上efSearch和metric_type,另外确认下collection的load状态,没load的话索引是不会驻留内存的。还有个思路是直接在Milvus的query日志里看下生成的执行计划,能直接看到是索引扫描还是暴力扫,这样定位快很多。
遇到过类似的坑,当时排查了半天发现是Milvus的索引状态没生效,你建完HNSW之后得手动调一下load_collection,不然查询时数据还在磁盘上没进内存,索引压根不会被触发,全量扫描就是这么来的。另外你说的“通过MCP调用”这个细节我觉得挺关键,如果MCP服务里每次查询都重新建立连接或者切换了collection,那可能索引就被绕过了,建议查一下查询参数里有没有显式指定一致性级别,有时候默认的强一致会强制走原始数据。还有个可能性是过滤条件写得太宽,比如把partition或标量字段的过滤加上了,而Milvus对带过滤的查询在特定版本里会降级成暴力扫描,这个坑在官方issue里也见过。你可以先在客户端直接用pymilvus跑同样的查询,对比下耗时,如果快很多那问题就出在MCP那层封装上,重点检查下查询的metric type和param是否传递完整。另外几万条数据其实不算多,就算全量扫理论上也就几百毫秒,如果慢到“快崩了”的程度,怀疑是MCP服务里把每次查询都当成了新任务在创建,导致向量数据反复从对象存储拉取,这个开销比扫描本身还大。建议把collection的加载状态和查询日志都打出来看看具体卡在哪一步,我之前就是靠这招定位到是MCP的异步处理没复用Milvus的连接池。
遇到过类似的坑,当时排查了半天发现是filter和vector检索混用的时候,Milvus的索引直接失效了,得看下你MCP那边是不是把查询条件包装成标量过滤了,HNSW只对纯向量查询生效。还有个可能,就是collection的metric type和索引参数没对齐,比如用了内积但索引写的是余弦,也会导致回退全扫描。建议先在Milvus的cli里直接用python client跑同样的查询,排除掉MCP层序列化的问题,如果cli也慢那就是索引建的有问题,得重建一下。
Milvus的HNSW索引没生效,大概率是查询参数里少了metric type或者索引参数没对齐,之前我也踩过这个坑,建索引的时候没指定metric type,默认用的L2,但查询的时候传的是IP,系统就直接fallback到全量扫描了,你检查下search请求里的params是不是跟索引定义完全一致。另外还有个可能,就是你灌数据之后没调flush,索引在Milvus里是异步构建的,数据还在内存buffer里没落盘,这时候查询即使有索引也会走暴力搜索,你可以试下先flush再查,看日志里segment状态是不是已经变成sealed了。还有个比较隐蔽的点,如果你的collection设置了partition key,但查询时没带partition,Milvus也会扫描所有分片,这跟MCP封装那层没关系,纯粹是查询路由的问题。要实在不行,可以直接用explain plan看看查询计划,Milvus新版支持这个,能明确告诉你是否命中索引,比看日志猜靠谱多了。
之前也踩过这坑,八成是MCP那边把查询当成标量过滤了,没走ANN,检查下过滤条件是不是写成了expr。
几万条数据就走全量扫描,大概率是查询时没走到HNSW索引。你确认下MCP调用时传的参数,比如nprobe或者ef这些,还有filter条件是不是把索引绕过了。我之前也踩过坑,Milvus里如果查询带了标量过滤,而且过滤字段没建索引,它会直接暴力扫。建议先看下query的explain,确认执行计划到底走了啥。
我之前也踩过这个坑,Milvus里索引建了但查询还是全扫,最后发现是搜索参数没配对。你检查下nprobe设了没,默认值有时候特别小或者干脆没生效,另外要确认下collection是不是load状态。还有个容易忽略的点,MCP那边传过来的查询向量维度跟建索引时对不对得上,维度不匹配也会直接退化成暴力搜索。建议先把Milvus的query日志级别调高,看看它实际执行的plan是什么。