type
status
date
slug
summary
tags
category
password

1、文档(Document)简介

Elasticsearch 中的文档(Document)是存储在索引中的基本数据单元,可以理解为是 Elasticsearch 中最小的数据实体。文档类似数据库的一条数据记录。文档以 JSON 格式表示,被索引后可以被搜索、查询和分析,每个文档都有一个唯一的 ID。
一个典型的文档可能如下所示:
文档的特性:
  • 不可变性:文档一旦被索引就是不可变的,更新操作实际上是删除旧文档并创建新文档。
  • 文档 ID:每个文档都有一个唯一 ID,用于标识和操作该文档。你可以自行指定,也可以让 Elasticsearch 自动生成。
  • _source 字段:在创建或更新文档时,原始的 JSON 数据默认会存储在 _source 字段中。查询时返回的完整文档数据就来自这里。是否存储 _source 会影响磁盘空间和使用方式。
  • 版本控制:Elasticsearch 为每个文档维护一个版本号。每次更改(创建、更新、删除)文档,版本号都会递增。支持乐观并发控制。

2、文档的处理过程

文档的操作 API 可以参考 Elasticsearch系列:REST API

2.1 新增(索引)文档的流程

当我们执行下面语句新增文档的时候,就会执行文档的索引流程。新增(索引)文档有两种方式:
Elasticsearch 的索引文档过程涉及从文档进入系统到最终被索引和搜索的全过程
第一阶段:文档写入(Write)阶段。包含以下处理阶段:
  1. 客户端请求:客户端通过 REST API 发送文档(PUT/POST), 该请求可以发送到集群中的任意节点 Node,此时 Node 节点就是协调节点(Coordinating Node)。协调节点是 Elasticsearch 集群中的一种特殊节点角色,负责处理客户端请求并将这些请求路由到正确的数据节点,协调节点本身不执行文本分析、索引或搜索等实际计算。默认情况下,所有节点都具备协调功能。
  1. 路由确定和确定目标分片:协调节点根据文档的 _id(如果请求中没有提供 _id,Elasticsearch 会自动生成一个) 确定目标分片,计算公式为shard_num = hash(routing) % num_primary_shards ,routing 默认是_id。这里用到的是主分片数量,这就是为什么一旦索引创建后,主分片的数量就不能更改的原因,否则会导致路由计算全部错误,数据就找不到了。
  1. 路由到主分片所在节点:协调节点根据路由计算的结果,将请求转发到承载该主分片(Primary Shard)的节点,由该节点执行预处理、文本分析和索引等操作。
  1. 主分片处理:主分片将数据写入到事务日志(Translog) 和内存缓冲区(In-memory Buffer)。此时,这个新索引的文档还不能被搜索到,因为它还在内存缓冲区中,还没有生成到可搜索的段(Segment)中。主分片的具体操作过程如下:
    1. 预处理阶段
        • 文档验证:检查 JSON 格式和字段类型
        • 动态映射:如果索引不存在或字段未定义,自动创建映射
        • 字段提取:解析文档中的各个字段
    2. 分析阶段(文本字段)
        • 字符过滤器:处理原始文本(如去除HTML标签)
        • 分词器:将文本拆分为词条(tokens)
        • Token过滤器:对词条进行处理(小写化、同义词、停用词等)
    3. 索引阶段
        • 倒排索引构建:创建 term → document 的映射
        • 正排索引构建:存储原始字段值用于聚合和高亮
        • Doc Values生成:列式存储用于排序和聚合
        • 存储原始文档:可选地存储整个原始文档
  1. 副本分片同步:主分片处理完成后,会并行地将请求转发到其所有副本分片。副本分片会执行和主分片完全一样的处理流程:写入 Translog + 内存缓冲区。每个副本分片完成操作后,会向主分片报告成功或失败。
  1. 主分片响应协调节点 :主分片等待足够数量的分片(包括主分片和副本分片,由wait_for_active_shards参数控制,默认为所有分片)响应成功,或者达到了配置的超时时间,会向最开始的协调节点发送一个成功响应。
  1. 协调节点响应客户端:最后,协调节点将成功响应返回给客户端。此时,客户端会收到一个确认,表示文档已经被成功索引(状态码 201)。
第二阶段:Refresh 阶段。在上面第 4 步完成后,文档虽然已经写入成功(写入了 Translog),但还不能被搜索到。为了使新索引的文档对搜索可见,需要进行 Refresh 操作(默认每 1 秒执行一次),将内存缓冲区中的内容写入到文件系统缓存(Filesystem Cache)中新创建的一个不可变的 Lucene 段(Segment),并清空内存缓冲区的内容。此时,文档就变得可被搜索了。
第三阶段:Flush 阶段。为了保证数据不会丢失,Elasticsearch 会定时将文件系统缓存中的段(Segment)持久化到磁盘,并清理事务日志(Translog)。到这一步,文档已经永久持久化了。
notion image

2.2 查找文档的流程

查找文档是指使用文档 ID 查找文档的过程,例如
查找文档的过程有点类似于索引文档的过程,因为有 routing 键,可以计算出数据所在的分片。
notion image
  1. Client 将请求发送到任意节点 node,此时 node 节点就是协调节点(coordinating node)。
  1. 协调节点根据以下计算公式获取目标分片: shard_num = hash(routing) % num_primary_shards ,routing 默认是文档的 id。
  1. 得到的目标分片是 primary shard,使用随机轮询算法,在 primary shard 和 replica shard 之间随机选择一个。
  1. 从选择的 shard 上获取文档,并把文档返回给协调节点。
  1. 协调节点向 Client 返回文档数据。

2.3 检索文档的流程

检索文档和查找文档的过程是不同的。
  • 查找数据是已知文档 ID,我们可以根据文档 id 直接查找文档内容。
  • 检索数据是根据某些关键字来检索,所以需要增加一个查找文档 id 的过程。
检索文档一般使用以下语句:
notion image
检索文档的过程分为两个阶段(Query Then Fetch):
  1. 查询阶段(Query Phase),目标是查询匹配的文档 ID。
    1. Client 将请求发送到任意节点 node,此时 node 节点就是协调节点(coordinating node)。
    2. 协调节点解析查询请求(包括分词、过滤、排序、分页等),将查询转换为 Lucene 查询语法。
    3. 协调节点将解析后的查询请求广播到索引的所有 shard,每个 shard 使用轮询算法(默认采用随机策略)在 primary shard 和 replica shard 选择一个。
    4. 每个 shard 在本地执行以下查询,将满足条件的数据(文档 ID、排序字段等)信息返回给协调节点。
      1. 使用倒排索引查找匹配的文档。如果对于关键词查询(如 "apple"),会直接去倒排索引中查找该词项(Term),得到包含该词项的所有文档 ID 的列表(Postings List);对于复杂查询(如布尔查询、范围查询),会组合多个词项的结果。
      2. 计算文档的相关性评分 _score(使用TF/IDF或BM25算法)
      3. 应用过滤条件、排序等
      4. 返回 Top N 结果。每个分片会返回 Top N(根据 from + size 参数决定)个文档的 ID 和评分给协调节点。分片内部会维护一个大小为 from + size 的本地优先级队列,只保留最相关的文档。
  1. 取回阶段(Fetch Phase),查找完整的文档数据。如果查询要求返回完整的文档内容(_source 字段),协调节点会查询文档 ID 对应的完整文档,这一阶段类似于文档的查找过程。
    1. 协调节点重新进行排序,截取数据后,获取到真正需要返回的文档 ID。
    2. 根据文档 ID 再次请求对应的 shard,获取文档返回协调节点。
    3. 协调节点向 Client 返回数据。

2.4 更新/删除文档的流程

Elasticsearch 中的文档是无法直接更新的。我们通常说的对 Elasticsearch 中的文档进行更新,实际上是对指定的文档进行重新索引,也就是将原有的文档进行标记删除,然后再重新索引一个新的文档
例如 id 为 1 的文档已经存在,我们仍然可以执行下面的更新语句
删除文档的过程:磁盘上的每个段都有一个相应的 .del 文件。当删除请求发送后,文档并没有真的被删除,而是在 .del 文件中被标记为删除。该文档依然能匹配查询,但是会在结果中被过滤掉。当段合并时,在 .del 文件中被标记为删除的文档将不会被写入新段。
更新文档的过程:在新的文档被创建时,Elasticsearch 会为该文档指定一个版本号,当执行更新时,旧版本的文档在 .del 文件中被标记为删除,新版本的文档被索引到一个新段。旧版本的文档依然能匹配查询,但是会在结果中被过滤掉。

3、近实时搜索机制

Elasticsearch 的近实时搜索(Near real-time search)是指向 Elasticsearch 插入文档后,不会立刻被搜索到,而是要在一段非常短的时间延迟(通常是 1s )后才能搜索到插入的文档。
Elasticsearch 不像关系型数据库一样,通过事务和隔离性来控制数据的可见,执行数据插入以后立刻就能搜索到刚刚插入的数据。Elasticsearch 主要用于大数据量和复杂场景的检索或聚合能力,近实时是为了在吞吐量、性能和数据安全性之间取得平衡而采用的设计。官方文档参考:ElasticElasticNear real-time search | Elasticsearch Guide [8.15] | Elastic

3.1 近实时的工作原理

在这里先介绍下:近实时搜索机制的几个重要概念:
  • Memory Buffer:内存缓冲区,Elasticsearch 每当有新写入数据,都将数据写入 Memory Buffer,在 Memory Buffer 中的数据既不能被搜索,也没有被持久化。
  • Filesystem Cache:Elasticsearch 定时(默认 1s)将 Memory Buffer 中的数据写入一个新的 segment 文件中,并进入操作系统缓存 Filesystem Cache,同时清空 Memory Buffer。在 Filesystem Cache 中的数据可以被搜索到,但是还没有进行持久化。所以 Elasticsearch 的新增数据的搜索并不是实时的,而是近实时搜索。
  • Translog:由于 Memory Buffer 和 Filesystem Cache 都是基于内存,假设服务器宕机,那么数据就会丢失,所以 Elasticsearch 通过 Translog 日志文件来保证数据的可靠性。在数据写入 Memory Buffer 的同时,将数据也写入 Translog 日志文件。当机器宕机重启时,Elasticsearch 会自动读取 Translog 日志文件中的数据,恢复到 Memory Buffer 和 Filesystem Cache 中去。Translog 以追加写的方式写入,所以性能很高。默认每隔 5s fsync 到硬盘,fsync 前如果宕机了最多丢失 5s 的数据。
数据落盘完整过程是 write -> refresh -> flush -> merge
notion image
  1. write 过程数据先写入内存缓冲区(Memory Buffer),同时写入磁盘上的事务日志(Translog),这时候数据还不能被搜索到。Translog 也是先写入 Filesystem Cache,然后默认每隔 5 秒刷一次到磁盘中,所以默认情况下,可能有 5 秒的数据会仅仅停留在 Memory Buffer 或者 Translog 文件的 Filesystem Cache 中,而不在磁盘上,如果此时机器宕机,会丢失 5 秒钟的数据。
    1. notion image
  1. refresh 过程每隔一段时间时间(由 index.refresh_interval 控制,默认1s),执行一次自动刷新(refresh)操作,将所有 Memory Buffer 的内容写入到 Filesystem Cache 的一个新的 Lucene 分段(Segment),然后清空 Memory Buffer 的内容。这个 Segment 是被打开且 uncommited 状态,这时候数据可以被搜索到,但是还没有被持久化。这就是“近实时”的来源:数据在写入约 1 秒后,就从内存缓冲区转移到了文件系统缓存中的一个可搜索的分段里。它还没有被物理写入磁盘,但已经可以被搜索到了。
    1. notion image
  1. flush 过程默认经过 30min 或者 Translog 达到一定大小(受 index.translog.flush_threshold_size 控制,默认 512MB),执行一次刷盘(flush)操作,将 Segment 从 Filesystem Cache 物理写入磁盘。具体操作是:先将 Memory Buffer 中所有的数据 refresh 到 Filesystem Cache 的新的 Segment 中,然后创建一个新的 commit point(提交点),提交点的作用是将 Filesystem Cache 的所有 Segment 落盘,最后删除旧的 Translog 并创建一个新的 Translog 日志文件。此时数据已经被持久化。
    1. notion image
  1. merge 过程:由于自动刷新流程每秒会创建一个新的 segment ,这样会导致短时间内的 segment 数量暴增。而 segment 数目太多会增加系统资源消耗。Elasticsearch 会在后台进行 merge 操作,小的 segment 被合并到大的 segment,然后这些大的 segment 再合并到更大的 segment。这就是段合并机制。
    1. notion image

3.2 控制近实时行为

你可以根据具体场景手动控制刷新行为,以优化性能。
  1. 手动刷新。可以对某个索引手动调用 refresh API,立即让缓冲区中的数据可搜索。这在演示或测试时有用,但不要在生产中频繁使用。
    1. 设置刷新间隔,太短的刷新间隔会增加性能开销。
      在导入大量日志或重建索引时,通常不需要每秒都能搜索到。你可以临时关闭刷新来大幅提升写入性能。
      1. 禁用自动刷新。
        1. 批量导入完成后,再恢复。
          Zookeeper系列:Zookeeper简介Elasticsearch系列:集群管理
          Loading...