内核调优 | 如何提升Elasticsearch master调度性能40倍

本文涉及的产品
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
简介: 自建集群从Elasticsearch 6.3.2 版本升至 7.4.0 版本,如何解决Master卡顿、创建/删除索引耗时过多的问题?本文给你答案。

作者:兴丰__阿里云Elasticsearch团队 高级开发工程师

本文字数:1299
阅读时间:2~5分钟

以下是正文


背景

我们在协助某Elasticsearch用户准备将自建集群迁往阿里云Elasticsearch的过程中发现,自建集群从ES6.3.2版本升级到7.4.0版本后,master变得特别卡,创建索引和删除耗时超过1分钟。该集群当时有3个专有主节点、10个热节点、2个冷节点,超过5万个shard,绝大部分索引/shard都是关闭的,当索引过期移动到冷节点就close掉,需要查询时再调用open命令打开。同时,在试过6.x到7.x的多个版本后,发现自7.2.0后的版本都有问题,而即使把专有主节点升级到32c64g的规格,还是不行。

思考

由于是自建的线上生产集群,登录机器和查看集群状态极为不便,也有一定的风险。因此计划先从Elasticsearch代码的变更入手,查找版本7.2的哪一个pull request最有可能改变master的调度行为,很快发现pr#39499引入的“支持对已关闭索引进行shard数据复制“有最大的嫌疑。

在引入pr#39499之前,一个索引关闭之后,数据节点对应的engine会关闭,不再提供查询和写入服务,当然也不能进行relocate和数据的拷贝,当下掉一个数据节点后,上面已关闭索引的shard数据就会丢失。

引入pr#39499之后,master会在cluster state中继续保留关闭的索引,对索引的shard进行调度,数据节点使用NoOpEngine打开索引,不支持查询和写入,和常规的engine相比开销很小。所以就转化为,集群状态存在很多shard时,master调度很慢的问题。

复现

搭建一个最小化测试环境

  • Elasticsearch版本: 7.4.0
  • 专有主节点: 3 * 16c64g
  • 数据节点: 2 * 16c64g

先创建5000个索引,每个索引有5个primary,0个replica,总共25000个shard。然后测试发现每次创建新索引需要58s。master所在机器有一个cpu利用率一直处于100%,通过top -Hp $ES_PID, 得到忙碌的线程ID。通过和jstack获取master节点的调用栈信息对比,发现是masterServices线程一直调用shardsWithState导致的。


"elasticsearch[iZ2ze1ymtwjqspsn3jco0tZ][masterService#updateTask][T#1]" #39 daemon prio=5 os_prio=0 cpu=150732651.74ms elapsed=258053.43s tid=0x00007f7c98012000 nid=0x3006 runnable  [0x00007f7ca28f8000]

  java.lang.Thread.State: RUNNABLE
       at java.util.Collections$UnmodifiableCollection$1.hasNext(java.base@13/Collections.java:1046)
       at org.elasticsearch.cluster.routing.RoutingNode.shardsWithState(RoutingNode.java:148)
       at org.elasticsearch.cluster.routing.allocation.decider.DiskThresholdDecider.sizeOfRelocatingShards(DiskThresholdDecider.java:111)
       at org.elasticsearch.cluster.routing.allocation.decider.DiskThresholdDecider.getDiskUsage(DiskThresholdDecider.java:345)
       at org.elasticsearch.cluster.routing.allocation.decider.DiskThresholdDecider.canRemain(DiskThresholdDecider.java:290)
       at org.elasticsearch.cluster.routing.allocation.decider.AllocationDeciders.canRemain(AllocationDeciders.java:108)
       at org.elasticsearch.cluster.routing.allocation.allocator.BalancedShardsAllocator$Balancer.decideMove(BalancedShardsAllocator.java:668)
       at org.elasticsearch.cluster.routing.allocation.allocator.BalancedShardsAllocator$Balancer.moveShards(BalancedShardsAllocator.java:628)
       at org.elasticsearch.cluster.routing.allocation.allocator.BalancedShardsAllocator.allocate(BalancedShardsAllocator.java:123)
       at org.elasticsearch.cluster.routing.allocation.AllocationService.reroute(AllocationService.java:405)
       at org.elasticsearch.cluster.routing.allocation.AllocationService.reroute(AllocationService.java:370)
       at org.elasticsearch.cluster.metadata.MetaDataIndexStateService$1$1.execute(MetaDataIndexStateService.java:168)
       at org.elasticsearch.cluster.ClusterStateUpdateTask.execute(ClusterStateUpdateTask.java:47)
       at org.elasticsearch.cluster.service.MasterService.executeTasks(MasterService.java:702)
       at org.elasticsearch.cluster.service.MasterService.calculateTaskOutputs(MasterService.java:324)
       at org.elasticsearch.cluster.service.MasterService.runTasks(MasterService.java:219)
       at org.elasticsearch.cluster.service.MasterService.access$000(MasterService.java:73)
       at org.elasticsearch.cluster.service.MasterService$Batcher.run(MasterService.java:151)
       at org.elasticsearch.cluster.service.TaskBatcher.runIfNotProcessed(TaskBatcher.java:150)
       at org.elasticsearch.cluster.service.TaskBatcher$BatchedTask.run(TaskBatcher.java:188)
       at org.elasticsearch.common.util.concurrent.ThreadContext$ContextPreservingRunnable.run(ThreadContext.java:703)
       at org.elasticsearch.common.util.concurrent.PrioritizedEsThreadPoolExecutor$TieBreakingPrioritizedRunnable.runAndClean(PrioritizedEsThreadPoolExecutor.java:252)
       at org.elasticsearch.common.util.concurrent.PrioritizedEsThreadPoolExecutor$TieBreakingPrioritizedRunnable.run(PrioritizedEsThreadPoolExecutor.java:215)
       at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@13/ThreadPoolExecutor.java:1128)
       at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@13/ThreadPoolExecutor.java:628)
       at java.lang.Thread.run(java.base@13/Thread.java:830)

分析&解决

通过阅读对应的代码,发现所有触发reroute操作的请求,比如创建、删除、更新集群状态等,都会调用BalancedShardsAllocator来遍历集群的所有started shard,再计算shard所在节点被relocated shard的磁盘占用大小。这需要找到节点所有处于INITIALIZING且正在relocated的shard,当前的实现是遍历节点的所有shard。

最外层对shard的遍历是O(n), 内层对每个节点的shard的循环是O(n/m), 其中n是集群shard总数,m是节点个数,整体复杂度是O(n^2/m),对一个固定节点个数的集群来说,m可以认为是常数,则每轮reroute的调度复杂度是O(n^2)。

考虑到每次都要重新遍历节点所有shard,寻找处于INITIALIZING和RELOCATING的shard,这部分可以在初始化时算一次,然后在每次shard有状态变化时,简单更新下即可,那么整体复杂度降为O(n)。对ES7.4.0的代码做了简单的修改、打包和测试,创建索引等触发reroute操作的请求时间从之前的58s降为1.2s,ES的hot_threads api和jstack都显示shardsWithState不再是热点,效果显著。

image.png

临时解决方案

master内部使用MasterService类来管理集群任务管理工作,为了保证状态的一致性,任务是单线程串行处理的,所以不能通过提升master节点的机器规格来解决。

当前的ES集群碰到这个问题,可以通过设置cluster.routing.allocation.disk.include_relocations为false来绕过,让master调度时不考虑正在relocating的shard磁盘占用。但是这会导致磁盘使用被错误估计,有反复触发relocate的风险。

相关活动


日志增强版_测试申请.png

扫码关注公众号‘Elasticsearch技术’,收获大咖最佳行业应用经验

image.png

往期好文

【产品解读】阿里云 Elasticsearch 在日志场景中实现“低成本高性能”
年度盘点 | “三年磨一剑” 阿里云Elasticsearch干货手册
Elasticsearch大咖说|携程旅行:从日志分析平台到综合性 Elasticsearch 管理平台

相关实践学习
使用阿里云Elasticsearch体验信息检索加速
通过创建登录阿里云Elasticsearch集群,使用DataWorks将MySQL数据同步至Elasticsearch,体验多条件检索效果,简单展示数据同步和信息检索加速的过程和操作。
ElasticSearch 入门精讲
ElasticSearch是一个开源的、基于Lucene的、分布式、高扩展、高实时的搜索与数据分析引擎。根据DB-Engines的排名显示,Elasticsearch是最受欢迎的企业搜索引擎,其次是Apache Solr(也是基于Lucene)。 ElasticSearch的实现原理主要分为以下几个步骤: 用户将数据提交到Elastic Search 数据库中 通过分词控制器去将对应的语句分词,将其权重和分词结果一并存入数据 当用户搜索数据时候,再根据权重将结果排名、打分 将返回结果呈现给用户 Elasticsearch可以用于搜索各种文档。它提供可扩展的搜索,具有接近实时的搜索,并支持多租户。
相关文章
|
7月前
|
机器学习/深度学习 搜索推荐 关系型数据库
号称Elasticsearch 10倍性能搜索引擎到底有多强悍
号称Elasticsearch 10倍性能搜索引擎到底有多强悍
236 0
|
3月前
|
缓存 固态存储 Java
Elasticsearch 的扩展性和性能调优
【9月更文第2天】Elasticsearch 是一个分布式的搜索和分析引擎,适用于各种大规模数据处理场景。随着数据量的增长和查询复杂度的增加,Elasticsearch 的性能优化变得尤为重要。本文将详细介绍如何通过硬件配置、集群规模调整以及查询优化策略来提升 Elasticsearch 的性能。
231 6
|
2月前
|
存储 缓存 监控
深入解析:Elasticsearch集群性能调优策略与最佳实践
【10月更文挑战第8天】Elasticsearch 是一个分布式的、基于 RESTful 风格的搜索和数据分析引擎,它能够快速地存储、搜索和分析大量数据。随着企业对实时数据处理需求的增长,Elasticsearch 被广泛应用于日志分析、全文搜索、安全信息和事件管理(SIEM)等领域。然而,为了确保 Elasticsearch 集群能够高效运行并满足业务需求,需要进行一系列的性能调优工作。
106 3
|
4月前
|
存储 负载均衡 算法
|
7月前
|
存储 JSON API
【Elasticsearch专栏 16】深入探索:Elasticsearch的Master选举机制及其影响因素分析
Elasticsearch,开源搜索和分析引擎,以其分布式特性受开发者喜爱。本文聚焦其Master选举过程,关键在于保障集群稳健和高可用。Master负责集群操作,数据节点存储数据。选举在Master不可用时发生,基于Zen Discovery模块,遵循多数派协议。选举过程包括启动发现、选举触发、节点投票和状态同步。相关命令和配置有助于管理选举和集群状态。理解和优化选举机制能提升Elasticsearch集群的性能和稳定性。
117 1
|
7月前
|
运维 测试技术 数据处理
Elasticsearch 优化查询中获取字段内容的方式,性能提升5倍!
Elasticsearch 优化查询中获取字段内容的方式,性能提升5倍!
68 0
|
7月前
|
监控 固态存储 安全
源码剖析:Elasticsearch 段合并调度及优化手段
源码剖析:Elasticsearch 段合并调度及优化手段
73 0
|
7月前
|
存储 缓存 运维
Elasticsearch 8.X 检索实战调优锦囊 001
Elasticsearch 8.X 检索实战调优锦囊 001
55 0
|
存储 缓存 固态存储
别再说你不会 ElasticSearch 调优了,都给你整理好了
别再说你不会 ElasticSearch 调优了,都给你整理好了
|
存储 缓存 固态存储
ElasticSearch深度调优指南大全
ElasticSearch深度调优指南大全
102 0

相关产品

  • 检索分析服务 Elasticsearch版