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 IndexInverted 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

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐