4、索引更新策略:全量重建与增量更新、实时索引与近实时索引、合并策略

索引建好了,数据却一直在变。这是搜索引擎运维里最让人头疼的事。

我记得刚接手一个电商搜索系统时,每天凌晨都要跑全量重建,耗时四五个小时。业务方抱怨说,商品价格改了,用户搜到的还是旧价格。嗯,这其实就是索引更新策略没选对。

今天咱们就聊聊这个话题。说白了,就是怎么让索引跟上数据变化的节奏。

4.1 全量重建 vs 增量更新

先讲最基础的两个策略。全量重建,就是把所有数据重新捞一遍,从头建索引。增量更新,则是只处理新增或变化的数据。

全量重建,我习惯叫它「笨办法但最稳」。为什么?

  • 索引结构最干净,没有碎片
  • 不会出现数据不一致
  • 适合数据量不大、更新不频繁的场景

但缺点也明显。数据量一大,全量重建就是噩梦。我在项目中遇到过,一个亿级文档的索引,全量重建要跑6小时。这期间用户搜到的都是旧数据。

增量更新,说白了就是只处理变化的部分。

  • 速度快,秒级甚至毫秒级
  • 资源消耗小
  • 适合高频更新的场景

但增量更新有个坑——删除操作。你想想看,文档删除了,但它在旧索引段里还留着。搜索引擎不会立即感知到。我曾经踩过这个坑,用户搜到了已下架的商品,点进去404。这体验太差了。

我的建议:全量重建和增量更新不是二选一。实际生产中,我通常这样搭配:

  • 日常用增量更新,处理实时变化
  • 每周或每月做一次全量重建,清理碎片、修复不一致

4.2 实时索引 vs 近实时索引

这两个概念容易混淆。我简单解释一下。

实时索引:数据一变,索引立刻更新。用户马上能搜到。

近实时索引:数据变了,但索引不是立刻更新,而是延迟几秒到几分钟。

为什么会这样?因为实时索引的成本太高了。

你想想看,每次数据变化都要立即更新倒排表、更新词典、更新统计信息。如果每秒有上万次更新,搜索引擎的CPU和IO直接被打满。

我见过一个极端案例:某社交平台做实时搜索,用户发帖后希望立刻被搜到。结果搜索引擎扛不住,查询延迟从10ms飙升到500ms。最后不得不改成近实时,延迟控制在3秒以内,用户体验几乎没差别,系统却稳多了。

特性 实时索引 近实时索引
延迟 毫秒级 秒级到分钟级
资源消耗 极高 中等
适用场景 金融交易、监控告警 电商、内容搜索
实现复杂度

避坑指南:我曾经在电商搜索里强行上实时索引,结果大促期间系统直接挂了。后来改成近实时,延迟设了5秒。用户根本感知不到,系统却稳如老狗。所以,别为了「实时」而实时,够用就好。

4.3 合并策略

增量更新会产生很多小索引段。每个段都是一个独立的索引文件。段越多,查询时合并结果的开销越大。

合并策略,说白了就是怎么把这些小段合并成大段。

常见的合并策略有三种:

  1. 按大小合并:小段优先合并,大段不动。适合写入量稳定的场景。
  2. 按时间合并:定期合并,比如每小时合并一次。适合有明确时间窗口的场景。
  3. 按文档数合并:段内文档数达到阈值就合并。适合文档大小不均匀的场景。

我个人的习惯是,用按大小合并为主,辅以定时触发。

举个例子,Elasticsearch 的合并策略就很有意思。它默认用 tiered 策略,说白了就是按大小分层合并。小段先合并成中等段,中等段再合并成大段。这样能避免大段频繁被合并,减少IO开销。

// 伪代码示例:合并策略配置
{
  "merge": {
    "policy": "tiered",
    "segments_per_tier": 10,    // 每层最多10个段
    "max_merged_segment": "5GB", // 单段最大5GB
    "max_segments": 50           // 最多保留50个段
  }
}

注意:合并操作非常消耗IO。我见过有人把合并线程数设得过高,结果磁盘IO被打满,查询延迟飙升。建议合并线程数不要超过CPU核数的一半。另外,合并期间要监控磁盘使用率,避免磁盘写满。

4.4 实战中的取舍

讲了这么多,其实核心就一句话:没有银弹

我总结了一个决策树,你可以参考:

  • 数据量小(百万级)→ 全量重建就够了,别折腾
  • 数据量大但更新少 → 全量重建 + 定时增量
  • 数据量大且更新频繁 → 增量更新 + 近实时 + 定期全量重建
  • 对实时性要求极高 → 实时索引 + 内存索引 + 异步落盘

最后说一句,索引更新策略不是一劳永逸的。业务在变,数据在变,策略也得跟着调。我每年都会复盘一次索引策略,看看有没有优化的空间。

嗯,这一章就到这里。下一章咱们聊聊索引的存储结构,那才是真正决定性能的地方。