搜索索引在线重建:别在全量扫描后直接切别名

122 次浏览3 条回复

改 mapping 或分词器时,通常会新建一个版本化索引再全量导入。麻烦在于导入期间数据库还在写:扫描结束就切别名,会漏掉这段时间的变更;让应用同步双写两个索引,又会把部分失败和重试乱序带进主链路。

一个比较稳的小做法,是让数据库事务只写业务表和 outbox,索引消费者按索引分别维护 checkpoint。新索引创建后先记下起始序号,用一致性快照做全量扫描;每条文档都带业务版本,并用 external version 写入。全量完成后,从 outbox 重放起始序号之后的变更,旧事件即使晚到,也不能覆盖新版本。outbox 的保留时间要覆盖最慢的一次全量导入,不能只按平时消费延迟设置。

切换前再取一个 cutover_seq,等新索引的 checkpoint 追到它,核对文档数、缺失主键和几组固定查询,然后原子切换读别名。切换后的事件仍由同一条可重放日志投递,不需要在应用请求里临时改双写逻辑。旧索引可以在观察窗口内继续消费,用于快速切回;删除前先确认两边 checkpoint 都越过观察窗口。

验收时可以故意打乱同一主键的两条变更、在全量中途重启消费者,并在别名切换前后持续写入。最终应满足:新索引没有漏掉 cutover_seq 之前的记录,旧版本事件不能覆盖新文档,切回旧索引时也不会回到观察窗口之前的数据。

补一条小经验:全量导入最好顺手把每批的最大业务版本、行数和耗时都记下来,后面一看就知道卡在哪一段。别名切换前做对账时,这几个数也挺好用,能更快定位是不是某批回放漏了。

隔壁七Lv1#1

补一条小经验:全量导入最好顺手把每批的最大业务版本、行数和耗时都记下来,后面一看就知道卡在哪一段。别名切换前做对账时,这几个数也挺好用,能更快定位是不是某批回放漏了。

再补一个小坑:切别名前最好把新索引的回放停点和快照点都记下来,别只记一个 cutover_seq。以后真要排查时,能立刻分清是全量漏了、回放慢了,还是切换窗口里来了新写入。

cedarmoonLv1#2

再补一个小坑:切别名前最好把新索引的回放停点和快照点都记下来,别只记一个 cutover_seq。以后真要排查时,能立刻分清是全量漏了、回放慢了,还是切换窗口里来了新写入。

对,最好两个点都记。cutover_seq 负责挡住切换后的新写入,快照点和回放停点则是排查窗口边界。后面真出问题时,先看是不是全量漏、回放滞后,还是切换时序没兜住,定位会快很多。