1、引言node
随着腾讯云 Elasticsearch 云产品功能愈来愈丰富,ES 用户愈来愈多,云上的集群规模也愈来愈大。咱们在平常运维工做中也常常会遇到一些因为前期集群规划不到位,致使后期业务增加集群规模大了以后带来的各类各样的集群可用性及稳定性问题。
这里列举下其中比较典型的几种集群规划问题:
性能优化
节点规格规划问题:集群数量很大,可是每一个节点的配置很低;网络
索引分片规划问题:索引很小,可是设置了几十个分片,或者索引很大,只设置了两三个分片;数据结构
分片数量规划问题:集群中包含 10万+ 的分片。架构
正所谓磨刀不误砍柴工,只有前期作好充分的集群评估规划工做,后期才能省去大量的运维工做。且可以长期保证集群的高可用和高稳定性。
本文结合咱们在给腾讯云 ES 集群平常运维工做中遇到的各类集群问题及总结沉淀的一些运维经验,来介绍下如何规划好集群容量及索引配置,以及所遵循的一些原则和经验。文章做者:吴容,腾讯云Elasticsearch研发工程师。
并发
2、集群规模及索引规划app
集群规模的评估主要评估如下三个方面:
第一,计算资源评估,计算资源的评估主要是评估单节点的CPU和内存。
ES的计算资源通常消耗在写入和查询过程,通过总结大量ES集群的运维经验,2C8G 的配置大概能支持 5k doc/s 的写入,32C64G 的配置大概能支撑 5w doc/s的写入能力。
第二,存储资源评估,存储资源的评估主要是评估磁盘的类型及容量大小。
例如ES集群使用什么类型的磁盘,SSD或者高性能云盘。以及每块盘的容量大小,是选择单盘多容量,仍是多盘少容量。而对于冷热分离的集群,则默认使用SSD做为热节点,高性能云盘做为温节点。
另外腾讯云ES支持单节点挂载多块云硬盘,且通过性能压测,3块盘相比于1块盘,吞吐量大约有2.8倍的提高。所以若是对写入速度及IO性能要求较高,可选择挂载多块 SSD 磁盘。
ES冷热分离多盘集群示意图
第三,节点数量评估,节点数量的评估主要是评估集群数据节点的数量。
在同等集群性能的状况下,建议优先选择高配置少节点的集群。例如 32C64G*3 节点的集群相比于 8C16G*12 节点的集群,在集群稳定性和扩容的便捷性上都有必定的优点。
由于高配置的集群若是遇到性能瓶颈须要扩容,则只须要横向扩容,即向集群中加入更多同等配置的节点便可;而低配置的集群在扩容节点配置时,则须要纵向扩容。
目前云上的纵向扩容方式有两种:
第一种是滚动重启方式扩容,这对集群稳定性会有必定的影响。
第二种是数据迁移方式扩容,其原理是先向集群中加入同等数量的高配置节点,而后将低配置节点上的数据迁移到新节点上,最后再将低配置节点剔除集群,因此这种扩容流程时间会比较长,且成本较高。
数据迁移方式纵向扩容示意图
运维
集群规模评估主要根据如下三点来评估:
异步
具体的业务场景,如日志分析、指标监控、搜索业务;分布式
业务预计的查询及写入QPS;
索引的数据总量。
这里结合咱们的运维经验,给出集群规模评估的几点参考建议:
32C64G单节点配置一般可承载5W次/s的写入;
写入量和数据量较大时,优先选择32C64G的节点配置;
1T的数据量预计需消耗2-4GB的内存空间;
搜索场景优先选择大内存节点配置;
存储容量 = 源数据 * (1 + 副本数量) * 1.45 * (1 + 预留空间) ≈ 源数据 * (1 + 副本数量) * 2.2.
索引配置的评估主要评估两点:
第一,如何划分索引?
在使用 index 时,建议作好按期切换索引的计划。对于日志场景来讲,写入不大的状况下建议按天建立索引,而写入较大的状况下,则建议按小时建立索引。
按期滚动索引的好处主要包括:可以控制单个索引的大小,提高读写性能;同时可以方式单个索引太大,影响故障恢复的时间;另外也能避免热索引过大,从而影响快照备份恢复的时间。
第二,如何设置索引主分片数?
云上的索引主分片数默认是5个,具体的大小则须要业务根据具体的场景及数据量来优化。下面会给出具体的一些准则和经验。
(2)根据什么评估?
索引配置的评估一样也要结合具体的业务场景及索引的数据量来评估,尤为是单日新增的数据量。
索引配置的评估可根据下面几点准则进行评估:
单个分片大小控制在 30-50GB;
集群总分片数量控制在 3w 之内;
1GB 的内存空间支持 20-30 个分片为佳;
一个节点建议不超过 1000 个分片;
索引分片数量建议和节点数量保持一致;
集群规模较大时建议设置专用主节点;
专用主节点配置建议在 8C16G 以上;
若是是时序数据,建议结合冷热分离+ILM 索引生命周期管理。
特别须要说明的是集群分片总数的大小控制上,咱们通过一些性能测试发现:当集群的总分片数超过 10w 个之后,建立索引时间会增加到分钟级。
尤为是对于写入量在百万 qps 以上的集群,若是总分片数在 10W+,且索引是自动建立的,那么就常常会在每次切换新索引时候,出现写入陡降、集群不可用的状况。
下面这张图是云上一个 100个节点,总分片数在 11W+ 的集群。天天 8点切换新索引时,写入直接掉0,集群不可用时间在数小时不等。
集群天天8点写入性能受到影响
对于这种问题,咱们腾讯云ES团队也有一些很是成熟的优化方案。
其中对于天天八点切换新索引时写入陡降的问题,可经过提早建立索引来解决,且建议使用固定的 index mapping,避免大量的 put-mapping 元数据更新操做。由于对于这种节点数量和总分片数量都很大的集群来讲,更新元数据是一个很是消耗性能的操做。
对于总分片数超过 10W 的问题,这种通常在日志分析场景中较为常见,若是历史数据不是很重要,则可按期删除历史索引便可。
而对于历史数据较为重要,任何数据都不能删除的场景,则可经过冷热分离架构+索引生命周期管理功能,将7天以前的数据存储到温节点,且在索引数据从热节点迁移到温节点时,经过 Shrink 来将主分片个数下降到一个较小的值,而且可将温节点数据经过快照方式备份到腾讯云COS中,而后将温节点上索引的副本设置为0,这样即可进一步下降集群中的总分片数量。
冷热分离+ILM+COS备份集群架构
2、ES写入性能优化
索引中每个 doc 都有一个全局惟一的 doc_id,这个 doc_id 可自定义,也可让ES自动生成。
若是自定义的话,则ES在写入过程当中会多一步判断的过程,即先Get下该 doc_id 是否已经存在。若是存在的话则执行 Update 操做,不存在则建立新的 doc。
所以若是咱们对索引 doc_id 没有特别要求,则建议让ES自动生成 doc_id,这样可提高必定的写入性能。
这一条优化建议在上面也提到了,由于建立索引及新加字段都是更新元数据操做,须要 master 节点将新版本的元数据同步到全部节点。
所以在集群规模比较大,写入qps较高的场景下,特别容易出现master更新元数据超时的问题,这可致使 master 节点中有大量的 pending_tasks 任务堆积,从而形成集群不可用,甚至出现集群无主的状况。
更新集群元数据超时
集群大量pending_tasks任务堆积
ES默认的 refresh_interval 是1s,即 doc 写入1s后便可被搜索到。
若是业务对数据实时性要求不高的话,如日志场景,可将索引模版的 refresh_interval 设置成30s,这可以避免过多的小 segment 文件的生成及段合并的操做。
愈来愈多的外部客户正选择将自建的ES集群迁移到腾讯云上来,客户一般是使用 logstash 来迁移数据,因为自建集群中完整保留了数据,所以这时候能够将云上的正在写入的索引副本设置为0, 这样可最快完成集群迁移工做。数据迁移完成后再将副本打开便可。
ES为了提高写入性能,提供了 Bulk 批量写入的API,一般客户端会准备好一批数据往ES中写入,ES收到 Bulk 请求后则根据routing 值进行分发,将该批数据组装成若干分子集,而后异步得发送给各分片所在的节点。
这样可以大大下降写入请求时的网络交互和延迟。一般咱们建议一次Bulk的数据量控制在10M如下,一次Bulk的doc数在 10000 上下浮动。ES Bulk请求示意图
上面咱们提到ES提供了Bulk接口支持将数据批量写入到索引,虽然协调节点是异步得将数据发送给全部的分片,可是却须要等待全部的分片响应后才能返回给客户端,所以一次Bulk的延迟则取决于响应最慢的那个分片所在的节点。这就是分布式系统的长尾效应。
所以,咱们能够自定义 routing 值,将一次Bulk尽可能转发到较少的分片上。
POST _bulk?routing=user_id
自定义routing
云上目前提供多种类型的磁盘可用选择,其中1T的 SSD 云盘吞吐量为 260M/s,高性能云盘为 150M/s。所以使用SSD磁盘对于写入性能和IO性能都会有必定的提高。
另外腾讯云如今也提供了多盘的能力,相对于单盘节点来讲,3块盘的吞吐量大约有2.8倍的提高。
咱们知道ES的索引有三种状态,分别是 Open状态、Frozen状态和 Close状态。以下图所示:
ES索引的三种状态
Open状态的索引因为是经过将倒排索引以FST数据结构的方式加载进内存中,所以索引是可以被快速搜索的,且搜索速度也是最快的。
可是须要消耗大量的内存空间,且这部份内存为常驻内存,不会被GC的。1T的索引预计须要消耗2-4GB的JVM堆内存空间。
Frozen状态的索引特色是可被搜索,可是因为它不占用内存,只是存储在磁盘上,所以冻结索引的搜索速度是相对比较慢的。若是咱们集群中的数据量比较大,历史数据也不能被删除,则能够考虑使用下面的API将历史索引冻结起来,这样即可释放出较多的内存空间。
POST /index_name/_freeze
对于冻结索引的搜索,能够在API中指定 ignore_throttled=false 参数:
GET /index_name/_search?ignore_throttled=false{ "query": { "match": { "name": "wurong" } }}
上面介绍了一些较为常见的写入性能优化的建议和经验,可是更为高效的优化还须要结合具体的业务场景和集群规模。
3、ES集群常规运维经验总结
ES集群的健康状态分为三种,分别是Green、Yellow和Red。
Green(绿色):所有主&副本分片分配成功;
Yellow(黄色):至少有一个副本分片未分配成功;
Red(红色):至少有一个主分片未分配成功。
咱们能够经过下面的API来查询集群的健康状态及未分配的分片个数:
GET _cluster/health{ "cluster_name": "es-xxxxxxx", "status": "yellow", "timed_out": false, "number_of_nodes": 103, "number_of_data_nodes": 100, "active_primary_shards": 4610, "active_shards": 9212, "relocating_shards": 0, "initializing_shards": 0, "unassigned_shards": 8, "delayed_unassigned_shards": 0, "number_of_pending_tasks": 0, "number_of_in_flight_fetch": 0, "task_max_waiting_in_queue_millis": 0, "active_shards_percent_as_number": 99.91323210412148}
其中须要重点关注的几个字段有 status、number_of_nodes、unassigned_shards 和 number_of_pending_tasks。
number_of_pending_tasks 这个字段若是很高的话,一般是因为 master 节点触发的元数据更新操做,部分节点响应超时致使的大量的任务堆积。
咱们能够经过下面的API来查看具体有那些 task 须要执行:
GET /_cat/pending_tasksinsertOrder timeInQueue priority source 1685 855ms HIGH update-mapping [foo][t] 1686 843ms HIGH update-mapping [foo][t] 1693 753ms HIGH refresh-mapping [foo][[t]] 1688 816ms HIGH update-mapping [foo][t]
其中 priority 字段则表示该 task 的优先级,翻看 ES 的源码能够看到一共有六种优先级:
IMMEDIATE((byte) 0),URGENT((byte) 1),HIGH((byte) 2),NORMAL((byte) 3),LOW((byte) 4),LANGUID((byte) 5);
当集群Red时候,咱们能够经过下面的API来查看分片未分配的缘由:
GET _cluster/allocation/explain
查看分片未分配的缘由
其中 index和shard 列出了具体哪一个索引的哪一个分片未分配成功。reason 字段则列出了哪一种缘由致使的分片未分配。这里也将全部可能的缘由列出来:
INDEX_CREATED:因为建立索引的API致使未分配。CLUSTER_RECOVERED :因为彻底集群恢复致使未分配。INDEX_REOPENED :因为打开open或关闭close一个索引致使未分配。DANGLING_INDEX_IMPORTED :因为导入dangling索引的结果致使未分配。NEW_INDEX_RESTORED :因为恢复到新索引致使未分配。EXISTING_INDEX_RESTORED :因为恢复到已关闭的索引致使未分配。REPLICA_ADDED:因为显式添加副本分片致使未分配。ALLOCATION_FAILED :因为分片分配失败致使未分配。NODE_LEFT :因为承载该分片的节点离开集群致使未分配。REINITIALIZED :因为当分片从开始移动到初始化时致使未分配(例如,使用影子shadow副本分片)。REROUTE_CANCELLED :做为显式取消从新路由命令的结果取消分配。REALLOCATED_REPLICA :肯定更好的副本位置被标定使用,致使现有的副本分配被取消,出现未分配。
detail 字段则列出了更为详细的未分配的缘由。下面我会总结下在平常运维工做中常见的几种缘由。
若是未分配的分片比较多的话,咱们也能够经过下面的API来列出全部未分配的索引和主分片:
GET /_cat/indices?v&health=red
the node is above the high watermark cluster setting [cluster.routing.allocation.disk.watermark.high=95%], using more disk space than the maximum allowed [95.0%], actual free: [4.055101177689788%]
当咱们执行 _cluster/allocation/explain 命令后看到上面的一行语句的话,则能够判断是该索引主分片所在的节点磁盘满了。
解决方法:扩容磁盘提高磁盘容量或者删除历史数据释放磁盘空间。
一般若是磁盘满了,ES为了保证集群的稳定性,会将该节点上全部的索引设置为只读。ES 7.x版本以后当磁盘空间提高后可自动解除,可是7.x版本以前则须要手动执行下面的API来解除只读模式:
PUT index_name/_settings{ "index": { "blocks": { "read_only_allow_delete": null } }}
failure IllegalArgumentException[number of documents in the index cannot exceed 2147483519
该限制是分片维度而不是索引维度的。所以出现这种异常,一般是因为咱们的索引分片设置的不是很合理。
解决方法:切换写入到新索引,并修改索引模版,合理设置主分片数。
cannot allocate because a previous copy of the primary shard existed but can no longer be found on the nodes in the cluster
这种状况一般是因为某个节点故障或者因为负载较高致使的掉线。
解决方法:找到节点掉线缘由并从新启动节点加入集群,等待分片恢复。
node does not match index setting [index.routing.allocation.require] filters [temperature:\"warm\",_id:\"comdNq4ZSd2Y6ycB9Oubsg\"]
解决方法:从新设置索引所需的属性,和节点保持一致。由于若是从新设置节点属性,则须要重启节点,代价较高。
例如经过下面的API来修改索引所须要分配节点的温度属性:
PUT /index_name/_settings{ "index": { "routing": { "allocation": { "require": { "temperature": "warm" } } } }}
cannot allocate because all found copies of the shard are either stale or corrupt
POST _cluster/reroute?pretty" -d '{ "commands" : [ { "allocate_stale_primary" : { "index" : "article", "shard" : 1, "node" : "98365000222032", "accept_data_loss": true } } ]}
reached the limit of incoming shard recoveries [2], cluster setting [cluster.routing.allocation.node_concurrent_incoming_recoveries=2] (can also be set via [cluster.routing.allocation.node_concurrent_recoveries])
这种状况一般出如今集群重启,或者某一个节点重启后。且因为设置的分片并发恢复的值较低致使。为了尽快恢复集群健康状态。
解决方法:能够经过调用下面的API来提高分片恢复的速度和并发度:
PUT /_cluster/settings{ "transient" : { "cluster.routing.allocation.node_concurrent_recoveries": "20", "indices.recovery.max_bytes_per_sec": "100mb" }}
结语