早期的搜索引擎系统确实通常是为整个文档集合建立一个大的倒排索引。这种方法在文档集合较小且变动不频繁的情况下效果尚可,但随着文档集合的扩大和更新频率的增加,逐渐暴露出一些严重的问题:
- 修改成本高:任何文档的细微修改都需要重新构建整个倒排索引。这不仅消耗大量计算资源,而且导致索引更新速度非常慢。
- 时效性差:为了降低索引重建的成本,可能会选择定期更新索引。然而,这样一来,索引的时效性就会受到影响,用户无法及时搜索到最新的数据。
- 系统性能瓶颈:随着数据规模的增加,重建整个倒排索引的时间和资源需求呈指数级增长,最终导致系统性能瓶颈。
为了解决这些问题,现代搜索引擎(如 ElasticSearch)采用了分段存储(Segment Storage)的策略,将一个大的索引文件拆分为多个独立的子文件,每个子文件称为一个段(segment)。这种方法有效地解决了上述问题,具体机制如下:
分段存储的工作机制
1.独立的不可变段:
- 数据被索引时,不会直接修改现有的段,而是将新的文档写入内存中的缓冲区(buffer)。当缓冲区满时,数据会刷新到磁盘,形成一个新的段。
- 段是不可变的,这意味着一旦段写入磁盘,就不会再修改它。这种设计简化了并发控制,提高了系统的稳定性。
2.段合并(Segment Merging):
- 随着时间的推移,会生成越来越多的小段。为了避免段的数量过多影响查询性能,ElasticSearch
会周期性地执行段合并,将多个小段合并成一个较大的段。 - 在合并过程中,会处理删除标记(删除标记的文档不会出现在新段中)和过期数据,从而保持索引的高效和紧凑。
3.灵活的更新和删除机制:
- 更新或删除文档时,ElasticSearch 并不会立即修改现有的段,而是将更新或删除的文档标记,并写入新的段。这样,只有与修改相关的段需要处理,不影响其他段。
- 删除操作通过在段中标记文档为已删除,实际删除会在段合并过程中进行。
修改和删除文档的机制
1.标记删除:
- 当需要删除文档时,ElasticSearch 不会立即从段中移除该文档,而是将其标记为删除。这意味着文档仍然存在于段中,但在搜索结果中不会被返回。
- 删除标记会在内存中维护一张删除表,记录哪些文档被标记为删除。
2.文档更新:
- 更新文档实际上是删除旧的文档并插入新的文档。具体步骤如下:
- 将旧文档标记为删除。
- 将新文档作为新的版本写入内存缓冲区,并在适当的时候刷新到磁盘生成新的段。
3.段合并:
- 段合并(Segment Merging)是清理标记删除文档的主要机制。当段合并发生时,ElasticSearch会将多个小段合并成一个大的段。在这个过程中,删除标记的文档不会被复制到新的段,从而真正实现删除。
- 段合并不仅能清理删除标记,还能优化存储和提高查询性能。