向量库批量写入与近实时索引构建流水线落地

日期:2026-08-17

一、项目背景

我们的知识库问答建在向量库上,每天要灌几十万条文档切块,来源是各业务的资料库和当天新增的工单。最早我们用最直白的写法,一条一条 upsert 进向量库,写完顺手建索引。小数据量时没感觉,量一上来就露馅,写入被索引构建卡住,前端搜新文档经常搜不到,运营以为同步挂了,天天找我们确认。那段时间我们一半精力花在解释为什么新内容看不见,而不是做正事。

二、落地场景

重做之后,写入和索引构建拆成两段。应用侧先往一个批量缓冲里攒数据,攒到一批或者到时间窗口就一次性批量写进去,写的是未建索引的原始向量。索引构建走异步流水线,后台按段合并、增量建索引,建完才把这批数据标成可检索。查询走独立的读链路,写入再忙也不影响正在发生的检索。运营在后台能看到每批数据的写入时间和可检索时间,不用再来问我们。

三、关键技术挑战与解决思路

首先要解决的是批量缓冲怎么攒才不丢不延迟。我们用了带确认机制的缓冲队列,应用侧写成功才算数,攒批阈值和超时双触发,避免低峰期一批数据躺半天不建索引。接着要解决的是索引构建不能拖慢查询,我们把构建放在从节点,主节点只服务查询,段合并选在低峰期,资源争抢就小了。还有一块难啃的是近实时可见性的保证,我们定义了从写入到可检索的目标延迟,比如一分钟,监控这个延迟而不是只看写入成功,一旦超了就告警。

案例片段(已脱敏): 批量写入与异步索引配置片段(字段示意):ingest:  buffer_batch: 2000  buffer_flush: 5s  write_path: raw_vector   # 先写未索引原始向量 index_build:  mode: async  merge_offpeak: true  target_visible_latency: 60s一次调优:原逐条写入耗时约 4.2s/千条,改批量后降到 0.6s/千条;新文档可见延迟从分钟级(常超 5 分钟)收进 60s 目标内。

四、效果数据

批量写入吞吐提升约七倍,原来卡住的写入现在几秒就进缓冲。索引构建耗时因为异步化不再阻塞写入,整体同步周期短了一大截。新文档可见延迟从经常超过五分钟收到目标的一分钟以内,运营再没来问为什么搜不到。查询延迟在写入高峰保持稳定,因为读写链路分开了。要说明,buffer_batch 我们调过,设太小批量优势没出来,设太大低峰期延迟又飙,最后落在两千这个中间值。

五、可复用经验总结

向量库写入这事,最直接的教训就是别一条条写,那是给自己找麻烦。批量缓冲加异步建索引,本质是把写入和检索两件事解耦,谁也别挡谁。近实时可见性一定要当指标盯,光看写入成功会骗人,用户感知的是搜不搜得到。读写分离那块我后来的看法是,哪怕量少也值得做,因为量是一天天涨的,等卡了再拆就晚了。这套后来我们给其他几个检索场景也铺了。

结语

向量库写入这事最直接的教训就是别一条条写,那是给自己找麻烦。批量缓冲加异步建索引,本质是把写入和检索解耦,谁也别挡谁。我们最早没分开,写入被索引构建卡住,运营天天来问为什么搜不到新内容,一半精力花在解释而不是做事。读写分离那块,我后来的看法是哪怕量小也值得做,因为量是一天天涨的,等卡了再拆就晚了,拆分本身也有成本。近实时可见性一定要当指标盯,光看写入成功会骗人,用户感知的是搜不搜得到,所以我们现在监控的是从写入到可检索的延迟而不是写入是否完成。buffer_batch 那个值我们调了好几轮,太小批量优势出不来,太大低峰期延迟又飙,最后落在两千这个中间值,配合五秒刷新,高低压都稳。索引构建我们放到从节点,段合并选低峰期,主节点只服务查询,资源争抢小了查询延迟才稳。我们早期一条条写把运营坑得不轻,天天被问为什么搜不到,后来拆分才消停。可见延迟定了一分钟目标,超过就告警,之前写完了就当成功,运营投诉才发现延迟。这层体感指标最容易被忽略,我们是用反复解释换来的教训。向量库写入看着小,其实最影响使用者的体感。段合并我们试过按大小合并和按时间合并,最后混着用,低峰期按时间、积压多了按大小,灵活些。读链路我们加了本地缓存挡热点查询,写入高峰时查询也不抖。可见延迟告警接了值班手机,超一分钟直接叫人,比看面板及时。这套跑顺之后运营再没来问为什么搜不到,我们的解释精力终于省下来了。

读写分离后我们加本地缓存挡热点查询,写入高峰时检索也不抖。段合并试过按大小也按过时间,最后混着用,低峰期按时间、积压多了按大小,灵活不少。我们把可见延迟告警直接接到值班手机,超过一分钟就呼叫,比守面板及时。运营再没来问为什么搜不到,省下的解释精力我们拿去做正事,这才是拆分真正的收益。