1. Lucene 是什么? (搜索引擎的“引擎” ⚙️)

  • 定义:Lucene 是一个由 Apache 软件基金会开发的、开源的、高性能的 Java 全文检索引擎工具包

  • 形态:它不是一个完整的应用或服务器,而是一个 Java 类库 (JAR包)。开发者可以将它集成到自己的 Java 应用中。

  • 核心能力

    • 提供了强大的 索引 (Indexing) 和 搜索 (Search) 功能。

    • 其底层核心是 倒排索引 (Inverted Index),这是实现快速全文检索的关键技术。

  • 局限性

    • 使用复杂:直接使用 Lucene 的 API 相对底层和繁琐。

    • 功能单一:只关注索引和搜索,不提供分布式、高可用、数据聚合等高级功能。

    • 非开箱即用:需要开发者自己编写大量代码来构建一个完整的搜索引擎应用。

核心概念梳理

在深入流程之前,必须理解几个关键概念的对应关系:

Elasticsearch 概念 Lucene 概念 解释
Index (索引) Directory (目录) 一个 ES 索引由多个分片组成,每个分片都是一个独立的 Lucene 实例。
Shard (分片) 完整的 Lucene 实例 一个分片就是一个功能完备的搜索引擎。它是 ES 数据分发和并行化的基本单位。
Segment (段) Segment (段) 一个 Lucene 索引由多个段组成。段是不可变的、独立的索引单元。
Inverted Index Inverted Index (倒排索引) Lucene 的核心数据结构,用于快速查找包含特定词项的文档。

关键点:ES 负责处理分布式的复杂性(如何将请求路由到正确的分片、如何汇总结果),而每个分片(Shard)内部则是一个独立的 Lucene 实例,负责本地的索引和搜索工作。


2. 写入(Indexing)数据的过程

当你向 ES 发送一个索引请求(例如 POST /my_index/_doc),整个流程如下:

ES 分布式层面 (协调与路由)

  1. 接收请求:客户端请求发送到集群中的任意一个节点。这个节点被称为 协调节点 (Coordinating Node)

  2. 路由计算:协调节点根据文档的 _id 通过一个哈希公式 (hash(_id) % num_primary_shards) 计算出这份文档应该存储在哪个 主分片 (Primary Shard) 上。

  3. 请求转发:协调节点将请求转发到持有该主分片的 数据节点 (Data Node)

Lucene 本地层面 (实际写入)

现在,请求到达了目标数据节点,该节点上的主分片开始工作。这部分是纯粹的 Lucene 操作:

  1. 写入内存缓冲区 (In-memory Buffer)

    • 文档首先被写入内存中的一个缓冲区。这里的数据还不能被搜索到。

    • 同时,为了数据安全,ES 会将这次操作记录到一个叫 Translog (事务日志) 的文件中。这是一种预写日志(Write-Ahead Log),即使节点宕机,重启后也可以通过 Translog 恢复数据。

  2. Refresh (刷新) -> 生成 Segment

    • 默认情况下,每隔1秒(可配置),内存缓冲区的数据会被 "refresh" 到文件系统缓存中,形成一个新的 段 (Segment)

    • 关键点:一旦数据被写入一个 Segment,它就变得 可以被搜索到 了。这就是 ES 被称为 “近实时 (Near Real-Time)” 的原因——从写入到可被搜索有秒级的延迟。

    • Segment 是 不可变 的。更新或删除文档实际上是在新的 Segment 中标记旧文档为“已删除”,而不是修改旧 Segment。

  3. Commit (提交/落盘)

    • 虽然数据通过 Refresh 已经可以被搜索,但它仍在文件系统缓存中。为了防止断电导致数据丢失,ES 会定期执行 commit 操作(或当 Translog 达到一定大小时触发)。

    • Commit 操作会调用 Lucene 的 commit 方法,将文件系统缓存中的所有 Segment 数据 强制刷写(fsync)到物理磁盘上

    • 一旦 Commit 完成,对应的 Translog 就可以被安全地清除了。

ES 分布式层面 (数据同步与响应)

  1. 同步到副本:主分片完成索引操作后,会将请求并行地转发给所有的 副本分片 (Replica Shards)。副本分片会执行与主分片完全相同的 Lucene 写入流程(步骤 4, 5, 6)。

  2. 返回响应:一旦足够数量的分片(主分片 + N个副本分片,可配置)完成了操作,主分片所在节点就会向协调节点报告成功,协调节点最终向客户端返回成功响应。

流程图示:

codeCode

Client -> Coordinating Node -> Primary Shard Node -> [1. Translog, 2. Memory Buffer]
                                                               |
                                                          (refresh, ~1s)
                                                               |
                                                     v
                                                    New Lucene Segment (Searchable)
                                                               |
                                                          (commit/fsync)
                                                               |
                                                     v
                                                    Disk (Durable)

3. 查询(Searching)数据的过程

当你向 ES 发送一个搜索请求(例如 GET /my_index/_search?q=...),流程如下,这个过程被称为 Scatter-Gather (分发-汇聚)

ES 分布式层面 (分发查询 - Scatter Phase)

  1. 接收请求:客户端请求同样到达一个 协调节点

  2. 广播查询:协调节点将查询请求广播到该索引 (my_index) 所涉及的 每一个分片(可以是主分片或副本分片,ES 会做负载均衡)。

Lucene 本地层面 (执行查询)

  1. 本地搜索:每个分片(一个独立的 Lucene 实例)在本地执行该查询。

    • Lucene 会查询自己持有的 所有 Segment

    • 它利用 倒排索引 快速找到包含查询词项的文档 ID 列表。

    • 对匹配的文档计算相关性得分 (_score)。

    • 每个分片都会生成一个根据得分排序的、包含文档 ID 和分数的 本地结果优先队列 (例如,返回前10条结果,它可能会先找出本地的前10条)。

ES 分布式层面 (汇聚结果 - Gather Phase)

  1. 结果汇聚:所有分片将自己的本地结果(只包含文档 ID 和分数,不包含完整文档)返回给协调节点。

  2. 全局排序:协调节点收集到所有分片的结果后,将它们合并并进行 全局重新排序,以找出真正的 Top N 结果(例如,从3个分片各收到的10条结果中,选出全局得分最高的10条)。

  3. 获取文档 (Fetch Phase):协调节点现在知道了最终要返回的文档的 _id。它会根据这些 _id,向持有这些文档的原始分片发起一个 Multi-GET 请求,以获取完整的文档内容 (_source)。

  4. 返回最终结果:协调节点将获取到的完整文档内容与排序结果合并,形成最终的 JSON 响应,返回给客户端。

流程图示:

codeCode

Client -> Coordinating Node
              |
              | (Broadcast Query)
              +----------------> Shard 1 (Lucene Search -> Local Top N)
              +----------------> Shard 2 (Lucene Search -> Local Top N)
              +----------------> Shard 3 (Lucene Search -> Local Top N)
              |
              | (Gather Results)
              <---------------- Shard 1 [IDs + Scores]
              <---------------- Shard 2 [IDs + Scores]
              <---------------- Shard 3 [IDs + Scores]
              |
              v
        Coordinating Node (Merge & Global Sort -> Final Top N IDs)
              |
              | (Multi-GET by ID)
              +----------------> Relevant Shards to fetch _source
              |
              <---------------- Full Documents
              |
              v
Client <--- Final Response

总结

  • 写入时:ES 负责 路由 到正确的主分片,并协调 副本同步;主分片内的 Lucene 负责将数据写入 内存、生成 Segment、并最终持久化到磁盘

  • 查询时:ES 负责将查询 分发 到所有相关分片,并 汇聚、排序 各分片的结果;每个分片内的 Lucene 负责利用 倒排索引 在本地的 Segment 集合上进行高效搜索。

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐