1. 倒排索引的核心原理
Elasticsearch 的索引构建基于倒排索引,这是一种高效的全文检索数据结构。倒排索引的核心思想是将文档内容分解为单词(Term),然后建立 Term 到文档的映射,而不是传统的文档到内容的正向索引。
-
分词(Tokenization) :当你向 Elasticsearch 提交一个文档(比如一个 JSON 对象),它会通过分析器(Analyzer)对文本字段进行分词。例如,句子 "Elasticsearch is fast" 可能被分解为 ["elasticsearch", "is", "fast"]。
- 分析器包括分词器(Tokenizer)和过滤器(Filter)。分词器负责切分文本,过滤器可以做小写转换、去停用词(如 "is")、词干提取等。
- 分词后的 Term 是倒排索引的基本单位。
-
倒排表构建 :每个 Term 会关联一个倒排列表(Posting List),记录该 Term 出现在哪些文档中,以及出现的位置信息(比如偏移量)。例如:
vbnetTerm: "elasticsearch" → [Doc1, Doc5, Doc7] Term: "fast" → [Doc1, Doc3]
这个映射就是倒排索引的核心。
2. 索引的写入流程
Elasticsearch 是一个分布式的搜索引擎,索引的构建不仅限于单机,还涉及到分片和副本机制。以下是详细步骤:
-
文档路由到分片:
- 当你写入一个文档时,Elasticsearch 根据路由规则(默认是文档 ID 的哈希值)决定它属于哪个主分片(Primary Shard)。例如,如果有 5 个分片,哈希值模 5 决定目标分片。
- 每个主分片负责一部分数据,主分片再同步到对应的副本分片(Replica Shard)。
-
内存缓冲与 Segment:
- 文档首先被写入内存缓冲区(In-Memory Buffer),同时记录到事务日志(Translog)中,以保证数据持久性。
- 内存缓冲区中的数据会定期(默认 1 秒,或缓冲区满时)刷新(Refresh)到磁盘上,形成一个新的 Segment。Segment 是一个小型的倒排索引文件,此时数据变得可搜索。
- Segment 是不可变的,一旦生成就不会修改。
-
合并(Merge):
- 随着时间推移,Segment 数量增加,Elasticsearch 会定期执行合并操作,将小的 Segment 合并成大的 Segment,并清理已被删除的文档(逻辑删除的文档只在合并时物理删除)。
- 合并过程类似于 LSM 树(Log-Structured Merge Tree)的设计,优化了写入性能。
3. 分布式架构的协作
Elasticsearch 的索引构建是分布式的,分片机制和节点协作是关键:
-
主分片与副本分片:
- 主分片负责处理写入请求,完成后将数据同步到副本分片。
- 副本分片不仅提供高可用性(主分片故障时可提升为新的主分片),还分担查询负载。
-
节点间协调:
- 集群中的协调节点(Coordinating Node)接收用户请求,路由到对应的分片所在的节点。
- 数据写入时,主分片所在的节点会确保事务日志和 Segment 的生成,然后通知副本分片同步。
4. 性能优化与权衡
- 近实时搜索:通过内存缓冲和 Refresh 机制,Elasticsearch 实现了近实时搜索(默认 1 秒延迟)。这是通过牺牲一定一致性换来的。
- 不可变 Segment:Segment 的不可变性提高了读取效率(无需锁),但增加了合并的开销。
- 分析器的灵活性:分词和过滤规则可以在索引创建时自定义,影响索引大小和查询性能。
5. 总结原理
Elasticsearch 构建索引的过程可以概括为:
- 文档经过分析器分词,生成 Term。
- Term 被组织成倒排索引,写入内存缓冲。
- 定期刷新生成不可变的 Segment,同步到分片。
- 通过合并优化存储和性能。
- 分布式分片和副本机制保证高可用性和扩展性。