「干货」图解 Elasticsearch 写入流程_es

整体上看,Client 向 ES 发送写请求,es 接收数据,写入磁盘文件,返回响应给 Client 写入成功,这样就完成了。

然后拉近看一下,看看内部都做了什么工作。

2. ES 整体结构

「干货」图解 Elasticsearch 写入流程_es_02

ES 集群里面有多个 Server 节点,一个 ES Index 有多个 shard 分片,每个 shard 有多个副本。

「干货」图解 Elasticsearch 写入流程_es_03

其中有一个 primary 主副本,负责写入,其他副本为 replica,不能写,只能同步 primary 的数据,但可以处理读请求。

ES 收到写请求后,会将请求路由到目标 shard 的 primary 副本。

「干货」图解 Elasticsearch 写入流程_es_04

每一个 shard 就是一个 Lucene Index,包含多个 segment 文件,和一个 commit point 文件。

segment 文件存储的就是一个个的 Document,commit point 记录着都有哪些 segment 文件。

「干货」图解 Elasticsearch 写入流程_es_05

了解了 ES 的整体结构,可知 primary shard 收到写请求,就是把 Document 数据写入 segment。

3. 直接写 Segment 效率低怎么办?

如果每次写操作都是直接落盘的话,I/O 效率会比较低。

「干货」图解 Elasticsearch 写入流程_es_06

所以,ES 使用了一个内存缓冲区 Buffer,先把要写入的数据放进 buffer。

内存性能好,但不安全,会丢数据,所以 es 使用了一个日志文件 Translog。就像 MySQL 的 Binlog,记录着每一条操作日志,如果 ES 出现故障,重启之后可以从 Translog 中恢复数据。

因为日志文件只是单纯的做内容追加,没有其他逻辑操作,所以写入速度很快。

并不是每条操作直接写入磁盘,也是有一个内存缓冲,每隔5秒写入磁盘。所以,极端情况下会有5秒数据的丢失,如果要严格保证数据安全,可以调整这个时间。

这样,数据来到 primary shard 之后,先是进入 buffer,并把操作记录写入 Translog。

4. Buffer 中数据怎么写入 Segment 文件?

「干货」图解 Elasticsearch 写入流程_es_07

ES 每隔一秒执行一次 refresh 操作,会创建一个 Segment 文件,将 buffer 中的数据写入这个 segment,并清空 buffer。

进入 segment 的数据就进入了 Lucene,建立好了倒排索引,可以被搜索到。

5. 如何进一步提升写 Segment 效率?

「干货」图解 Elasticsearch 写入流程_es_08

需要注意,ES 虽然把数据写入了 segment 文件,但实际上还没有真正落盘,因为操作系统的文件系统也是有缓存的,这是操作系统层面的性能优化,由操作系统决定物理落盘时间,除非程序写文件时使用同步写 fsync。

ES 为了性能自然没有使用 fsync,因为有 Translog 记录了所有操作步骤。

虽然可能还没实际写入 segment 物理文件,但只要进入了操作系统的文件系统,就可以被搜索了,这一点不用担心。

6. Translog 日志文件越来越大怎么办?

「干货」图解 Elasticsearch 写入流程_es_09

随着写入数据的增加,Translog 也越来越大,需要清理。

触发清理动作需要2个条件:

  1. 大小触发设定的阈值

  2. 30分钟

任意条件满足即触发一次 commit 提交操作。

「干货」图解 Elasticsearch 写入流程_es_10

操作流程:

  1. 执行 refresh 操作。

  2. 把这次提交动作之前所有没有落盘的 segment 强制刷盘,确保写入物理文件。

  3. 创建一个提交点,记录这次提交对应的所有 segment,写入 commit point 文件。

  4. 清空 Translog,因为 Segment 都已经踏实落地了,之前的 Translog 就不需要了。

这个提交流程,称为 flush

7. Segment 太多怎么办?

通过上面的流程,可以发现,segment 文件太多了,一秒就产生一个,这会严重影响搜索性能。

「干货」图解 Elasticsearch 写入流程_es_11

es 有一个后台程序,用于 merge 合并这些 segment 文件,把小 segment 整合到一个大的 segment 中,并修改 commit point 的 segment 记录。

merge 过程还会清理被删除的数据。

es 接收到删数据请求时,不会真的到 segment 中把数据删了,而是把要删除的数据写到 '.del' 文件中,在读操作时,会根据这个文件进行过滤。

merge 合并时才真正删除,合并后的 segment 中就没有已经删除的数据了。

8. 小结

写操作时,ES 把写请求路由到目标 Shard 所在的 Server 节点,Doc 先被放入 Buffer,并记录此请求的操作日志,写入 Translog。

每隔一秒,执行 Refresh 操作,将 Buffer 中数据写入 Segment 文件,并清空 Buffer。

Refresh 实际写入的 Segment 文件缓存,会在 Flush 操作中刷盘到物理文件。

当 Translog 过大,或者每隔 30 分钟,执行一次 Flush 操作。

先执行一次 Refresh,然后将所有 Segment 文件缓存刷盘到物理文件,并创建提交点,记录此次操作设计的 Segment,写入 Commit Point 文件,最后清空 Translog。

ES 的 Merge 程序负责合并小 Segment,并清除被删除的 Document。

京东到家订单中心系统mysql到es的转化之路

搜索引擎技术选型调研:Elasticsearch与Solr

「轻阅读」ZK和EureKa的区别(CAP理论)

「轻阅读」亿级用户的分布式数据存储解决方案