写点什么

E 往无前|腾讯云大数据 ES 索引原理剖析及写入性能优化最佳实践

  • 2023-12-28
    广东
  • 本文字数:3499 字

    阅读完需:约 11 分钟

E往无前|腾讯云大数据ES索引原理剖析及写入性能优化最佳实践

导读

本文经过大量案例总结和踩坑复盘,归纳整理了 Elastisearch 集群在写入性能优化方面一些常用的优化技巧和避坑指南。


在我们服务腾讯云 ES 的客户过程中,经常会收到一些客户对云上 ES 集群读写性能未能达到预期的反馈,并希望我们能够配合做一些性能压测及调优的工作。经过我们结合客户的业务场景和深入分析集群性能瓶颈后,基本都可以给出客户一些能够明显提升读写性能的建议和优化措施。

腾讯云大数据 ES 团队通过众多大客户集群的实践经验,归纳整理了 Elastisearch 集群在写入性能优化方面一些常用的优化技巧和避坑指南。


ES 集群索引原理剖析

在介绍 ES 集群写入性能优化之前,我们先来简单回顾下 Elastisearch 集群索引的基本流程。如下图 1 所示。



图 1. ES 集群索引写入流程示意图

通过分析图 1 的左半部分,可以看出 ES 写入基本流程如下:

1、客户端向 Node1 发送索引文档请求;

2、Node1 节点根据 doc_id 确定文档属于分片 0,将请求转发到 Node3

3、Node3 在主分片上执行请求,成功后将请求并行转发到副本分片所在节点。一旦所有副本分片都写入成功,则 Node3 向协调节点返回写入成功,协调节点向客户端报告 doc 文档写入成功。

而通过分析图 1 的右半部分,我们可以看出写入到 ES 集群的文档在一个分片上的具体执行逻辑,主要分为如下图 2 所示的四个关键步骤。



图 2. ES 集群写入核心流程示意图


1、Wirte 过程:

分片所在的节点在接收到协调节点的写入请求后,首先会将 doc 写入到一个叫 In-memory Buffer 中,也被称为 Indexing Buffer。这时候 doc 还是不可搜索的状态,当 In-Menory Buffer 空间被写满后,会自动将 In-Memory Buffer 中的 doc 生成一个 segment 文件,这时候 doc 才会被搜索到。

2、Refresh 过程:

上面的 write 过程中,doc 会被首先写入到 In-Memory Buffer 中,In-Memory Buffer 中的数据会在两种情况下被清空:第一种情况就是上面说的当 In-_memory Buffer 空间被写满了之后,ES 会自动清空并生成一个 Segment,第二种情况就是 ES 定时触发的 Refresh 操作,默认是 1s 执行一次。Refresh 过程其实就是将 In-Memory Buffer 中的数据写入到 Filesystem Cache 中的过程,同时该过程也会生成一个 Segment 文件。

3、Flush 过程:

上面的 Write 过程是将 doc 写入到 In-Memory Buffer 中,而 Refresh 过程是将 Doc 刷新到文件系统缓存中。这两个过程的数据都还是在内存中,一旦机器故障或宕机重启,都有可能会出现数据丢失风险。ES 为了数据丢失,通过 translog 机制来保障数据的可靠性,实现机制是接收到写入请求后同时将 doc 以 key-value 形式写入到 translog 文件中,当 Filesystem Cache 中数据被刷到磁盘中才会清空,其中 Filesystem Cache 中数据刷盘的过程就是 Flush 操作。从图 2 中也可以看出,执行了 Flush 操作后,In-Memory Buffer 和 translog 中的数据都会被清空。

4、Merge 过程:

ES 的每一次 Refresh 操作,都会生成一个新的 Segment 段文件。这样会导致在短时间内段文件数量的暴涨。而段数目太多会带来一系列的问题,比如会大量消耗文件句柄,消耗内存和 cpu 运行周期。并且每个搜索请求都必须轮流检查每个段文件,导致搜索性能下降明显。为了解决这个问题,Elasticearch 通过在后台维护一个线程来定期的合并这些 Segment 文件,将小的段合并到大的段,然后再将大的段合并到更大的段。这个合并的过程就是 Segment Merge 的过程。


ES 集群写入性能优化

1、时序类场景结合 ILM 动态滚动索引写入

对于日志、监控、APM 等场景建议结合索引生命周期管理(ILM)和快照生命周期管理(SLM)。通过 ILM 来灵活控制写入索引的大小,尤其是当遇到流量突增时,不至于出现单个索引容量过大而导致影响写入性能的情况。

我们云上的一些客户经常会组织一些运营活动,比如电商类的购物节等,在运营活动期间,业务产生的日志量往往是平常的数倍甚至十几倍。如果按照每天创建一个索引的话,活动期间当天的索引将会变得很大,很容易超过官方推荐的“单分片大小控制在 30-50GB”的设计原则。由此会触发一系列的问题,甚至会影响影响性能。例如当单个分片过大,将会影响分片搬迁的速度和节点异常重启后分片恢复的效率,甚至会提前触发单分片 21 亿条 doc 数的上限,从而导致数据丢失。

而结合索引生命周期管理,我们可以灵活设计索引滚动的策略,例如一天后开始滚动、索引达到 1T 后开始滚动,doc 数量达到 10 条开始滚动,任何一个条件先触发都会滚动到下一个新索引写入。这样就能够很好的控制每个索引和分片的大小在一个稳定的范围内。关于索引生命周期管理的更多细节可参考“腾讯云 Elasticsearch 索引生命周期管理原理及实践”。

2、写入数据时不指定 doc_id,让 ES 自动生成

指定 doc_id 写入会在写入前先检查该 doc 是否存在,存在则会做 update 操作,不存在则会做 insert 操作,因此指定 doc_id 写入会对集群的 CPU、负载和磁盘 IO 的消耗都比较大。我们之前有对比分析过一家社区团购大客户在指定 doc_id 和不指定 doc_id 写入的写入性能数据。在指定 doc_id 写入的情况下,CPU 使用率上升了 30%,IOutils 上涨了 42%。

3、使用 bulk 接口批量写入数据,每次 bulk 数据量大小在 10M 左右

ES 为了提升写入性能,提供了一个叫 bulk 批量写的 API,该 API 的写入允许客户端一次发送多条 doc 文档给协调节点进行写入。而批量写入的 doc 大小或者 doc 数量直接决定了写入性能高低的关键因素。经过我们大量测试以及官方的建议,每次 bulk 写入数据量控制在 10M 是比较理想的,以一条 doc1k 来测算的话,一批 bulk 的 doc 数量建议控制在 8000-20000 之间。我们可以通过_tasks API 来查看设置的 doc 条数是否在一个合理的范围内。

GET_tasks?detailed=true&human&group_by=parents&actions=indices:data/write/bulk
复制代码



图 3.  _tasks API 查看 bulk 写入详情


4、开启 bulk_routing,将请求转发到较少的分片

默认情况下,客户端的一批 bulk 写入发送到协调节点后,协调节点在本地先根据一定的策略按照分片的个数将这批 doc 分成若干份,并行发送到各个主分片所在的节点,然后等待所有分片所在节点返回写入成功 ack 后,协调节点才会返回给客户端。因此,这一批 bulk 的写入耗时将直接取决于响应耗时最慢的节点,这就是典型的长尾效应。

而为了解决这一问题,腾讯云 ES 团队自研了 bulk_routing 高级特性。通过在协调节点随机生成一个 routing,将每一批 bulk 写入都只转发到特定的一个分片节点上,通过这种方式来降低写入过程中的网络开销和 CPU 使用率,避免长尾分片影响整体的写入性能。我们可以通过如下 API 来开启 bulk_routing,经过我们观察开启了 bulk_routing 的某大客户日志集群发现,相对于不开启 bulk_routing,写入峰值提升了 25%,CPU 使用率下降了 20%。并且在节点个数越多时,性能提升越明显。

PUT my-index/_settings{    "index.bulk_routing.enabled": true}
复制代码

5、规模较大集群,提前创建好索引,使用固定 Index mapping

集群规模较大,通常索引容量、分片总数都会比较大,由于在创建索引和更新 mapping 时涉及到 meta 元数据的更新,因此会在 master 更新元数据的过程中短暂阻塞写入,如果此时写入量很大,则有可能会导致写入掉零。下图 4 是我们云上一客户在每天早上 8 点切换索引时,更新 mapping 超时导致写入掉零的日志截图。而如果能提前创建好索引,以及使用固定的索引模版,则可以避免在索引切换时大量的元数据更新操作,从而保障集群的稳定性和写入性能。



图 4. 集群更新 mapping 超时截图


6、提前设置好 mapping,并减少索引字段数量

对于明确不会参与检索的字段,尤其是 binary 字段和超长的 text 字段,可以将索引字段的 index 属性设置为 not analyzed 或者 no,也就是说我们让 es 不要对这些字段进行分词和构建索引,通过这种方式可以减少不必要的运算,降低 CPU 性能开销,从而提升集群的写入性能。下图 5 是我们做的一次性能测试,当我们把所有字段属性都设置了 index 后, 再次进行写入压测时,可以看到 CPU 使用率一路攀升到 90%。



图 5. 所有字段开启分词和索引后集群的 cpu 使用率情况


7、追求写入性能的场景,可以将正在写入的索引设置为单副本,写入完成后打开副本

我们经常会遇到一些客户在两个集群或者两个索引之间做一些数据同步的工作,例如通过 logstash 来做集群之间的数据迁移,通过 reindex API 来做索引之间的索引重建工作。在这种快速导数据场景下,可以先将目标端的索引副本关闭,因为在数据迁移这种场景,由于源索引中已经存在一份原始数据了,因此不必担心关闭副本后数据丢失的问题。等迁移完成后再将副本打开即可。同时我们也可以将 refresh_interval 时间设置得大一些,如 30s。目的是可以减少 segment 文件的生成。以及 segment 文件的合并次数,从而降低 CPU 和 IO 的性能开销,保障集群的写入性能。

以上我们深入剖析了 ES 集群文档索引的基本原理和流程,同时也结合了腾讯云 ES 众多大客户的运维经验和踩坑教训,总结了 7 点写入性能优化相关的建议。希望能对腾讯云 ES 的每一个客户的都有所帮助。

用户头像

还未添加个人签名 2020-06-19 加入

欢迎关注,邀您一起探索数据的无限潜能!

评论

发布
暂无评论
E往无前|腾讯云大数据ES索引原理剖析及写入性能优化最佳实践_ES_腾讯云大数据_InfoQ写作社区