我们从 1.x 开始使用 Elasticsearch ,发展到现在大大小小的集群有 5000+,最大的集群物理主机 100+,单集群最大数据量几百 TB,几千亿条 doc。在这个大规模的应用过程中我们积累了很多宝贵经验,在此与大家分享。
相比 Hadoop 系列的大数据平台,Elasticsearch 使用起来要简单得多,你只要修改很少的几个配置就可以让集群运行起来,而且 Elasticsearch 拥有丰富的 REST 接口,你能够看到集群的各种指标和状态,从而了解集群内部的运行状态。
但越是简单易用的系统,它的内部就越隐含着更多的默认配置。系统可以在默认配置下正常运行,但是随着数据量的增加,或者用户访问的高峰期,集群可能忽然出现问题 —— 昨天还是好好的,为什么今天就出问题了?我们什么都没动。
要解决这些问题,你就需要知道故障的根本原因是什么,这需要你对系统有足够的了解。在大型互联网公司中,都有做基础技术的团队负责平台的开发和运维,但中小型企业很少会有这样的团队。
尤其像 Hadoop 这种复杂的系统,没有基础技术团队你很难把它真正用起来。虽然 Elasticsearch 简便许多,但仍然可能会遇到一些问题,排查这些问题可能会耽误你很长时间。
另一方面,一个新业务准备使用 Elasticsearch 时对集群的规划往往没有概念:
我们往往只是在遇到线上故障的时候才去分析相关原理,而不是空闲的时候进行系统的学习。分析定位问题虽然有趣,但有时我们更希望有人直接告知答案是什么,它为什么会这样,怎么做可以合理地解决问题。
我们写下这门课程最大的目的,是希望读者能够解决用户的这些烦恼,分享我们在日常运维过程中所遇到的常见问题,大部分的故障是类似的。把这些分享出来,避免他人再重新分析问题。让读者能够合理地规划、使用,以及监控集群。
这些经验是我们在多年开发、运维 Elasticsearch 集群中的原理和实践的沉淀。如果只知道原理,没有实践经验,可能会在遇到问题时不知道具体该怎么做,更不明白原理的内在联系和利害关系,而实践经验可以让读者理解原理的同时有一个更具体的、可实施的参考依据。
点击了解《高可用 Elasticsearch 集群 21 讲》。
本课程由四部分组成,总计 21 篇。
第一部分(第 1-1~1-4 课)新集群诞生
合理地规划集群规模、组织结构,以及根据业务特点设计索引和分片是第一步。实际上很多问题是由于集群规划不合理,或者分片配置不合理、mapping 配置不合理导致的。
一开始的时候就把这些工作做好,可以避免很多后期问题。这部分内容根据我们多年维护经验给读者一些原则性的参考,让集群规划和分片规划等能够相对准确地量化出来。
第二部分 (第 2-1~2-9 课)集群管理
新集群组建好之后,最好对集群性能做整体评估,虽然基准测试有一定参考价值,但由于业务数据的差异,往往导致较大的性能差别,因此业务最好根据自己的数据样本进行压力测试,这样你就明确的知道系统能够达到什么能力,做到心中有数。
这部分内容介绍了如何进行压力测试和对比压测效果,以及后续的日常管理工作,包括集群监控、应该重点关注哪些指标、性能优化都需要做些什么、集群什么时候需要扩容、扩容注意事项、数据迁移等常见的管理工作。
第三部分 (第 3-1~3-4 课) 安全防护
业务初期很容易忽视安全问题,在遇到麻烦的时候才去解决。安全包括用户认证、用户鉴权、通讯加密等多方面的内容,最简单的方式可以使用 Nginx 之类的反向代理来过滤请求,实现简单的用户和权限管理。
X-Pack 中提供了非常完善的安全组件,但是版本之间的变化比较大,中文内容又比较少,这部分内容就是使用 X-Pack 进行安全防护,并接入自己的用户管理体系。
第四部分 (第 4-1~4-4 课)常见问题
在这部分中我们把多年运维过程中常见的、比较通用的问题梳理出来,希望给读者一个参考,在遇到类似问题的时候可以从中直接找到答案,以及分析问题的思路和方法、诊断问题所需的工具等,把握住问题的本质。
虽然一个故障可能是多种因素导致的,这部分内容无法覆盖所有的情况,但是相信可以应对大部分情况。
本课程内容属于进阶到精通级别,需要读者有一定的基础知识,在基础知识方面,《Elasticsearch 权威指南》已经很好,我们希望给读者一些切合实际的、注重实战方面的资料。
因此,本课程重点解决以下实际问题:
在阅读本课程之前,建议读者已经阅读过《Elasticsearch 权威指南》中的大部分内容,对 Elasticsearch 的基本概念如分片、节点角色,以及常见配置项已经有了一定的了解。
关于 Elasticsearch 基础知识的内容,例如:分布式搜索,主从节点,主副分片等基本概念有很多资料可以参考,由于本课程定位为进阶与精通,在此我们不再重复介绍这些基础知识。
如果读者尚未了解这些基础概念,或者知识体系尚不完善,推荐阅读 Elasticsearch 官方的资料,现在,我们给读者一些具体学习建议,方法论可以让你把握住重点,提升效率。
分布式系统是复杂的,但是 Elasticsearch 的产品设计使得它变得简单易用。当你下载到一个安装包,不用修改任何配置就可以让它运行起来,相对于 Hadoop 系列的平台来说容易的多。
但是当你大规模应用于线上服务时,只使用默认配置是不够的,你需要根据自己的情况进行很多调整,你可以等系统出现问题的时候再去了解相关知识,但是对于入门级用户来说,最好先系统化地熟悉 Elasticsearch 的基础知识。
小白用户该如何学习 Elasticsearch 呢?
我们推荐阅读《Elasticsearch 权威指南》,虽然它的内容有些过时,但仍然是最适合入门者的材料,建议从第一页开始通读此书,并且搭建一个集群实际操作一下,这样会有一个直观的感受,加深理解和记忆。读书过程中会产生很多问题,因此可以自己做一些测试,或者与其他人互相交流探讨解决疑问。
每当读完一章,或者一个比较大的部分,你需要用自己的方式整理这部分的重点,可以是思维导图、PPT 或者 Markdown 等你喜欢的形式,这样当读完整本书的时候,你会形成自己的知识体系结构,通过查看笔记就可以掌握所有的关键环节。
学习的过程中一定要有输出,用自己的语言将学习到的知识表达出来。我们团队的同事们在学习完一部分内容后会在周会的时候做一次技术分享,虽然在制作 PPT 的时候会花些时间,但这是一个对知识整理的过程,你需要把它给大家讲明白。
在讲解的时候会面对大家的很多问题,在整个归纳和整理的过程中,你自己就发现很多疑问,连自己的说服不了,这就迫使你去彻底搞明白。但如果你只是被动地阅读,没有主动思考,理解的层次就往往流与表面,并且容易忘记。
这个过程进度不用太快,优先消化和吸收知识点,保证学习效果。最理想的是同时配合一些实践,虽然你刚刚学习完一大部分的内容,但是在线上遇到与这部分相关的问题时可能还是会有点懵,因此处理一些实际问题会让你对系统的理解更加深刻。
有些问题解决起来确实比较花时间,每次解决必须搞清楚到底什么原理,切忌模棱两可的结论,它可能是这样,也可能是那样,虽然有时候搞清楚到底怎么回事可能花费非常多的时间,但是一旦解决,以后你再也不会受它的困扰,否则后续遇到类似问题仍然是原地踏步。
这个时期的参考资料不建议去 baidu 查阅中文博客的文章,因为这些资料经常有很多一知半解的错误,并且内容过时。
官网的 API 手册、GitHub 上的 Issues、Pull Request,以及中文及英文社区是最权威的内容,绝大部分问题都可以找到答案。尤其是在 API 的使用方式上,官网的手册是非常完整的,并且很多 API 在不同版本之间存在较大差异,因此最好在手册中选择与自己一致的版本。
对于集群故障,也尽量不要百度查找答案,搜索技术内容 Google 要要准确的多。另外,有条件的话也可以报名参加官方的培训课程,并考取 Elasticsearch 工程师认证。官方的培训是线下的形式,基于最新的稳定版,并且对于学习中的问题可以现场得到最权威的解答,是效率最高的学习方式。
当你系统化地学习过基础知识,并且有了一些线上故障处理经验,进一步的学习需要关注更多内容,有时候你会遇到比较深层次的问题,虽然在《Elasticsearch 权威指南》中有许多原理方面的知识,但是仍然不够,有些问题你需要从源码中找到答案。
进阶方面的内容可以阅读我的另一本书《Elasticsearch 源码解析与优化实战》以及本课程。系统化的学习,以及自己的亲身经历让你成长很多,现在可能集群也比较稳定,你也许不知道接下来应该再学些什么?怎样进一步提升?
现在是时候拓宽眼界,关注社区的发展,以及别人都遇到了哪些问题,不再局限于自己所处的环境。因此建议定期关注以下内容:
此外,建议多参加 Meetup 及技术交流会等线下活动,很多问题还是线下交流效果更好,毕竟 PPT 上的语言太过简练,同时也可以得到比较权威的答案。
很多问题是大规模应用的时候才会遇到,当集群数据量比较小,请求也比较少的时候,Elasticsearch 基本不会出问题。而大规模应用的企业一般都在比较大型的互联网公司,线下聊一聊很大程度上让你可以对业界情况有一定了解。
我们将从入门到进阶的知识体系大致归纳如下:
努力吧,你也将成为 Elasticsearch 专家!同时也建议提交一些有价值的 PR,共同促进 Elasticsearch 发展。
本课程着重讨论 Elasticsearch 在实际应用场景中所遇到和关心的问题。下一节将介绍如何规划新集群。
Elasticsearch 本身是一个分布式存储系统,也有一些特点像 NoSQL,同时又是一个全文检索系统,这就需要掌握更多的知识,同时可以思考一些其他类似系统的特点,例如在分段合并方面 HBase 是怎么做的,在磁盘管理、坏盘处理方面 HDFS 是怎么做的,等等。
由于 Elasticsearch 在使用方面的自由度比较大,我们希望告诉读者合理的部署和使用方式应该是什么样的,并且对于常见问题给出分析方法和解决方式,但是希望读者能够更多地思考背后的原理,掌握技术本质。大数据平台的很多理论都是相通的。
一个技术问题有多种解决方式,希望读者深入思考,关心背后原理,而非只是解决眼前问题。
为了方便学习和技术交流,特此创建了高可用 Elasticsearch 集群的读者群,入群方式写在第 1-3 课文末,欢迎已购本课程的同学入群交流。
点击了解《高可用 Elasticsearch 集群 21 讲》。
当有一个新的业务准备使用 Elasticsearch,尤其是业务首次建设 Elasticsearch 集群时,往往不知道该如何规划集群大小,应该使用什么样的服务器?规划多少个节点才够用?
集群规模当然是越大越好,但是出于成本考虑,还是希望集群规模规划的尽量准确,能够满足业务需求,又有一些余量,不建议规划一个规模“刚刚好”的集群,因为当负载出现波动,或者一些其他偶然的故障时,会影响到业务的可用性,因此留一些余量出来是必要的。
Elasticsearch 节点有多种角色,例如主节点,数据节点,协调节点等,默认情况下,每个节点都同时具有全部的角色。对于节点角色的规划我们将在下一个小节讨论,首先考虑一下我们需要多少个数据节点存储我们的数据。规划数据节点数量需要参考很多因素,有一些原则可以帮助我们根据业务情况进行规划。
数据总量是指集群需要存储的数据总大小,如果每天都有新数据入库,总量随之相应地增加。我们之所以要考虑数据总量,并非因为磁盘空间容量的限制,而是 JVM 内存的限制。
为了加快搜索速度,Lucene 需要将每个段的倒排索引都加载到 JVM 内存中,因此每一个 open 状态的索引都会在 JVM 中占据一部分常驻内存,这些是 GC 不掉的,而且这部分空间占用的比较大,并且由于堆内存不建议超过 32G,在磁盘使用率达到极限之前,JVM 占用量会先到达极限。
按照我们的经验,Elasticsearch 中 1TB 的 index 大约占用 2GB 的 JVM 内存,具体和字段数据类型及样本相关,有些会更多。一般情况下,我们可以按照这个比例来做规划。如果想要精确计算,业务可以根据自己的样本数据入库 1TB,然后查看分段所占的内存大小(实际上会比 REST 接口返回的值高一些)
curl -X GET "localhost:9200/_cat/nodes?v&h=name,segments.memory"
以 1TB 数据占用 2GB 内存,JVM 堆内存配置 31G ,垃圾回收器 CMS 为例,新生代建议配置 10G,old 区 21G,这 21G 内存分配一半给分段内存就已经很多了,想想看还没有做任何读写操作时 old 区就占用了一半,其他几个内存大户例如 bulk 缓冲,Query 缓存,indexing buffer,聚合计算等都可能会使用到 old 区内存,因此为了保证节点稳定,分段内存不超过 10G 比较好,换算成索引数据量为5TB。
因此,我们可以按照单个节点打开的索引数据总量不超过 5TB 来进行规划,如果预计入库 Elasticsearch 中的数据总量有 100TB 的数据(包括副分片所占用空间),那么数据节点的数量至少应该是: 100/5=20,此外出于冗余方面的考虑,还要多加一些数据节点。冗余节点的数量则和日增数据量以及故障转移能力相关。
可以看出这样规划出来的节点数是相对比较多的,带来比较高的成本预算。在新的 6.7 及以上的版本 Elasticsearch 增加了冻结索引的特性,这是一种冷索引机制,平时他可以不占内存,只有查询的时候才去加载到内存,虽然查询慢一些,但是节点可以持有更多的数据总量。
因此,如果你想要节点存储更多的数据量,在超出上述原则后,除了删除或 close 索引之外,一个新的选择是将它变成冻结状态。
单个节点可以持有的最大分片数量并没有明确的界限,但是过多的分片数量会造成比较大的管理压力,官方给出的建议是,单个节点上所持有的分片数按 JVM 内存来计算:每 GB 的内存乘以 20。例如 JVM 内存为 30GB,那么分片数最大为:30*20=600个。当然分片数越少会越稳定。
但是使用这个参考值也会有些问题,当分片大小为 40GB 时,节点所持有的数据量为:40 * 600 = 24TB,按照 1TB 数据量占用 2GB JVM 内存来计算,所占用 JVM 内存为:24 * 2 = 48GB,已经远超 JVM 大小,因此我们认为一般情况下不必将单个节点可以持有的分片数量作为一个参考依据,只需要关心一个原则:让 JVM 占用率和 GC 时间保持在一个合理的范围。
考虑另一个极端情况,每个分片的数据量都很小,同样不必关心每个节点可以持有多少,对大量分片的管理属于主节点的压力。一般情况下,建议整个集群的分片数量建议不超过 10 万。
一个 Elasticsearch 节点默认拥有所有的角色,分离节点角色可以让集群更加稳定,尤其是更加注重稳定性和可用性的在线业务上,分离节点角色是必要的。
集群有唯一的活跃主节点,他负责分片管理和集群管理等操作,如果主节点同时作为数据节点的角色,当活跃主节点失效的时候,例如网络故障,硬件故障,新的主节点当选后需要重新分配原主节点上持有的分片数据,导致集群在一段时间内处于 RED 和 YELLOW 状态。而独立部署的主节点不需要这个过程,新节点当选后集群可以迅速 GREEN。
另外,由于数据节点通常有较大的内存占用,GC 的影响也会导致混合部署的工作受到影响。因此如果集群很在意稳定性和可用性,我们建议数据节点有 3 个及以上时,应该独立部署 3 个独立的主节点,共 6 个节点。
有时候,你无法预知客户端会发送什么样的查询请求过来,也许他会包括一个深度聚合,这种操作很容易导致节点 OOM,而数据节点离线,或者长时间 GC,都会对业务带来明显影响。
亦或者客户端需要进行许多占用内存很多的聚合操作,虽然不会导致节点 OOM,但也会导致节点 GC 压力较大,如果数据节点长时间 GC,查询延迟就会有明显抖动,影响查询体验。
此时最好的方式就是让客户端所有的请求都发到某些节点,这种节点不存储数据,也不作为主节点,即使 OOM 了也不会影响集群的稳定性,这就是仅查询节点(Coordinating only node)。
Elasticsearch 支持在将数据写入索引之前对数据进行预处理、内容富化等操作,这通过内部的 processor 和 pipeline 实现,如果你在使用这个特性,为了避免对数据节点的影响, 我们同样建议将他独立出来,让写请求发送到仅预处理节点。
独立节点的各个角色后的集群结构如下图所示,其中数据节点也是独立的。
如果没有使用预处理功能,可以将读写请求都发送到协调节点。另外数据写入过程最好先进入 Kafka 之类的 MQ,来缓冲一下对集群的写入压力,同时也便于对集群的后期维护。
本文介绍了如何规划集群节点数和集群节点角色,依据这些原则进行规划可以较好的保证集群的稳定性,可以适用于组件新集群时评估集群规模,以及在现有集群接入新业务时对集群资源的评估。
关于索引和分片的规划将在后续章节中介绍。
为了方便学习和技术交流,创建了高可用 Elasticsearch 集群的读者群,入群方式在第 1-3 课,欢迎已购课程的同学入群交流。
点击了解《高可用 Elasticsearch 集群 21 讲》。
Elasticsearch 开箱即用,上手十分容易。安装、启动、创建索引、索引数据、查询结果,整个过程,无需修改任何配置,无需了解 mapping,运作起来,一切都很容易。
这种容易是建立在 Elasticsearch 在幕后悄悄为你设置了很多默认值,但正是这种容易、这种默认的设置可能会给以后带来痛苦。
例如不但想对 field 做精确查询,还想对同一字段进行全文检索怎么办?shard 数不合理导致无法水平扩展怎么办?出现这些状况,大部分情况下需要通过修改默认的 mapping,然后 reindex 你的所有数据。
这是一个很重的操作,需要很多的资源。索引设计是否合理,会影响以后集群运行的效率和稳定性。
当我们决定引入 Elasticsearch 技术到业务中时,根据其本身的技术特点和应用的经验,梳理出需要预先明确的需求,包括物理需求、性能需求。
在初期应用时,由于对这两方面的需求比较模糊,导致后期性能和扩展性方面无法满足业务需求,浪费了很多资源进行调整。
希望我总结的需求方面的经验能给将要使用 Elasticsearch 的同学提供一些帮助,少走一些弯路。下面分别详细描述。
根据我们的经验,在设计 Elasticsearch 索引之前,首先要合理地估算自己的物理需求,物理需求指数据本身的物理特性,包括如下几方面。
业务所涉及的领域对象预期有多少条记录,对 Elasticsearch 来说就是有多少 documents 需要索引到集群中。
每条数据的各个属性的物理大小是多少,比如 1k 还是 10k。
明确数据集中是否有长文本,明确长文本是否需要检索,是否可以启用压缩。Elasticsearch 建索引的过程是极其消耗 CPU 的,尤其对长文本更是如此。
明确了长文本的用途并合理地进行相关设置可以提高 CPU、磁盘、内存利用率。我们曾遇见过不合理的长文本处理方式导致的问题,此处在 mapping 设计时会专门讨论。
根据上面估算的数据总量和单条数据大小,就可以估算出预期的存储空间大小。
这里主要明确数据是以何种方式纳入 Elasticsearch 的管理,比如平稳增加、定期全量索引、周期性批量导入。针对不同的数据增量方式,结合 Elasticsearch 提供的灵活设置,可以最大化地提高系统的性能。
数据生命周期指进入到系统的数据保留周期,是永久保留、还是随着时间推移进行老化处理?老化的周期是多久?既有数据是否会更新?更新率是多少?根据不同的生命周期,合理地组织索引,会达到更好的性能和资源利用率。
使用任何一种技术,都要确保性能能够满足业务的需求,根据上面提到的业务场景,对于 Elasticssearch 来说,核心的两个性能指标就是索引性能和查询性能。
Elasticsearch 索引过程需要对待索引数据进行文本分析,之后建立倒排索引,是个十分消耗 CPU 资源的过程。
对于索引性能来说,我们认为需要明确两个指标,一个是吞吐量,即单位时间内索引的数据记录数;另一个关键的指标是延时,即索引完的数据多久能够被检索到。
Elasticsearch 在索引过程中,数据是先写入 buffer 的,需要 refresh 操作后才能被检索到,所以从数据被索引到能被检索到之间有一个延迟时间,这个时间是可配置的,默认值是 1s。这两个指标互相影响:减少延迟,会降低索引的吞吐量;反之会增加索引的吞吐量。
数据索引存储后的最终目的是查询,对于查询性能需求。Elasticsearch 支持几种类型的查询,包括:
结构查询主要是回答 yes/no,结构化查询不会对结果进行相关性排序。如 terms 查询、bool 查询、range 查询等。
全文检索查询主要回答数据与查询的相关程度。如 match 查询、query_string 查询。
无论结构化查询和全文检索查询,目的都是找到某些满足条件的结果,聚合查询则不然,主要是对满足条件的查询结果进行统计分析,例如平均年龄是多少、两个 IP 之间的通信情况是什么样的。
对不同的查询来说,底层的查询过程和对资源的消耗是不同的,我们建议根据不同的查询设定不同的性能需求。
此处索引设计指宏观方面的索引组织方式,即怎样把数据组织到不同的索引,需要以什么粒度建立索引,不涉及如何设计索引的 mapping。(mapping 后文单独讲)
如果查询中有大量的关于时间范围的查询,分析下自己的查询时间周期,尽量按照周期(小时、日、周、月)去组织索引,一般的日志系统和监控系统都符合此场景。
按照日期组织索引,不但可以减少查询时参与的 shard 数量,而且对于按照周期的数据老化、备份、删除的处理也很方便,基本上相当于文件级的操作性能。
这里有必要提一下 delete_by_query
,这种数据老化方式性能慢,而且执行后,底层并不一定会释放磁盘空间,后期 merge 也会有很大的性能损耗,对正常业务影响巨大。
检查查询语句的 filter 情况,如果业务上有大量的查询是基于一个字段 filter,比如 protocol,而该字段的值是有限的几个值,比如 HTTP、DNS、TCP、UDP 等,最好把这个索引拆成多个索引。
这样每次查询语句中就可以去掉 filter 条件,只针对相对较小的索引,查询性能会有很大提高。同时,如果需要查询跨协议的数据,也可以在查询中指定多个索引来实现。
如果查询语句中有比较固定的 filter 字段,但是该字段的值又不是固定的,我们建议在创建索引时,启用 routing 功能。这样,数据就可以按照 filter 字段的值分布到集群中不同的 shard,使参与到查询中的 shard 数减少很多,极大提高 CPU 的利用率。
我们强烈建议在任何业务中都使用别名,绝不在业务中直接引用具体索引!
索引别名就像一个快捷方式,可以指向一个或者多个索引,我个人更愿意把别名理解成一个逻辑名称。
对于无法预估集群规模的场景,在初期可以创建单个分片的索引 index-1,用别名 alias 指向该索引,随着业务的发展,单个分片的性能无法满足业务的需求,可以很容易地创建一个两个分片的索引 index-2,在不停业务的情况下,用 alise 指向 index-2,扩展简单至极。
业务中难免会出现需要修改索引 mapping 的情况,修改 mapping 后历史数据只能进行 reindex 到不同名称的索引,如果业务直接使用具体索引,则不得不在 reindex 完成后修改业务索引的配置,并重启服务。业务端只使用别名,就可以在线无缝将 alias 切换到新的索引。
对于像日志等滚动生成索引的数据,业务经常以天为单位创建和删除索引。在早期的版本中,由业务层自己管理索引的生命周期。
在 Rollover index API 出现之后,我们可以更方便更准确地进行管理:索引的创建和删除操作在 Elasticsearch 内部实现,业务层先定义好模板和别名,再定期调用一下 API 即可自动完成,索引的切分可以按时间、或者 DOC 数量来进行。
在正式接入业务数据之前进行合理的索引设计是一个必要的环节,如果偷懒图方便用最简单的方式进行业务数据接入,问题就会在后期暴露出来,那时再想解决就困难许多。
下一节我们开始介绍索引层面之下的分片设计。
为了方便学习和技术交流,创建了高可用 Elasticsearch 集群的读者群,入群方式在第 1-3 课,欢迎已购课程的同学入群交流。
点击了解《高可用 Elasticsearch 集群 21 讲》。
一个 shard 本质上就是一个 Lucene 索引,也是 Elasticsearch 分布式化 Lucene 的关键抽象,是 Elasticsearch 管理 Lucene 文件的最小单位。
所以,Elasticsearch 提供了大量的接口,可以对集群内的 shard 进行管理。
将分片从一个节点移动到另一个节点,在使用 Elasticsearch 中,鲜有需要使用该接口去移动分片,更多的是使用 AllocationDecider 参数以及平衡参数去自动调整 shard 的位置。
在一些特别的情况下,例如发现大部分热点数据集中在几个节点,可以考虑手工 move 一下。
curl -XPOST 'localhost:9200/_cluster/reroute' -d '{ "commands" : [ { "move" : { "index" : "test", "shard" : 0, "from_node" : "node1", "to_node" : "node2" } } ]}'
explain api 是 Elasticsearch 5.x 以后加入的非常实用的运维接口,可以用来诊断 shard 为什么没有分配,以及 shard 为什么分配在某个节点。
curl -XGET "http://localhost:9200/_cluster/allocation/explain { "index": "myindex", "shard": 0, "primary": true }
如果不提供参数调用该 api,Elasticsearch 返回第一个 unassigned shard 未分配的原因。
GET /_cluster/allocation/explain
在索引过程中,Elasticsearch 首先在 primary shard 上执行索引操作,之后将操作发送到 replica shards 执行,通过这种方式使 primary 和 replica 数据同步。
对于同一个分片的所有 replicas,Elasticsearch 在集群的全局状态里保存所有处于同步状态的分片,称为 in-sync copies。
如果修改操作在 primary shard 执行成功,在 replica 上执行失败,则 primary 和 replica 数据就不在同步,这时 Elasticsearch 会将修改操作失败的 replica 标记为 stale,并更新到集群状态里。
当由于某种原因,对于某个 shard 集群中可用数据只剩 stale 分片时,集群会处于 red 状态,并不会主动将 stale shard 提升为 primary shard,因为该 shard 的数据不是最新的。这时如果不得不将 stale shard 提升为主分片,需要人工介入:
curl -XPOST "http://localhost:9200/_cluster/reroute" -d '{ "commands":[{ "allocate_stale_primary":{ "index":"my_index", "shard":"10", "node":"node_id", "accept_data_loss":true } }] }'
当由于 lucene index 损坏或者磁盘故障导致某个分片的主副本都丢失时,为了能使集群恢复 green 状态,最后的唯一方法是划分一个空 shard。
curl -XPOST "http://localhost:9200/_cluster/reroute" -d '{ "commands":[{ "allocate_empty_primary":{ "index":"my_index", "shard":"10", "node":"node_id", "accept_data_loss":true } }] }'
一定要慎用该操作,会导致对应分片的数据完全清空。
一般来说,增加主分片数量可以增加写入速度和查询速度,因为数据分布到了更多的节点,可以利用更多的计算和 IO 资源。增加副分片数量可以提升查询速度,并发的查询可以在多个分片之间轮询。
但是 shard 管理并不是 “免费” 的,shard 数量过多会消耗更多的 cpu、内存资源,引发一系列问题,主要包括如下几个方面。
任一时刻,一个集群中只有一个节点是 master 节点,master 节点负责维护集群的状态信息,而且状态的更新是在单线程中运行的,大量的 shard 会导致集群状态相关的修改操作缓慢,比如创建索引、删除索引,更新 setting 等。
单个集群 shard 超过 10 万,这些操作会明显变慢。集群在恢复过程中,会频繁更显状态,引起恢复过程漫长。
我们曾经在单个集群维护 30 多万分片,集群做一次完全重启有时候需要2-4个小时的时间,对于业务来说是难以忍受的。
查询很多小分片会降低单个 shard 的查询时间,但是如果分片过多,会导致查询任务在队列中排队,最终可能会增加查询的整体时间消耗。
Elasticsearch 协调节点接收到查询后,会将查询分发到查询涉及的所有 shard 并行执行,之后协调节点对各个 shard 的查询结果进行归并。
如果有很多小分片,增加协调节点的内存压力,同时会增加整个集群的 cpu 压力,甚至发生拒绝查询的问题。因为我们经常会设置参与搜索操作的分片数上限,以保护集群资源和稳定性,分片数设置过大会更容易触发这个上限。
设置合理的分片数
创建索引时,可以指定 number_of_shards
,默认值是 5,对于物理大小只有几个 GB 的索引,完全可以设置成更小的值。
shard 合并
如果集群中有大量的 MB、KB 级分片,可以通过 Elasticsearch 的 shard 合并功能,将索引的多个分片合并成 1 个分片。
删除无用索引根据业务场景,每个索引都有自己的生命周期。尤其对于日志型索引,超过一定时间周期后,业务就不再访问,应该及时从集群中删除。
控制 replica 数量
replica 可以提高数据安全性,并可以负载读请求,但是会增加写入时的资源消耗,同时使集群维护的分片数成倍的增长,引起上面提到的诸多问题。所以要尽量降低 replica 数量。
Elasticsearch 通过 AllocationDecider 策略来控制 shard 在集群内节点上的分布。
same shard allocation decider
控制一个 shard 的主副本不会分配到同一个节点,提高了数据的安全性。
MaxRetryAllocationDecider
该 Allocationdecider 防止 shard 分配失败一定次数后仍然继续尝试分配。可以通过 index.allocation.max_retries 参数设置重试次数。当重试次数达到后,可以通过手动方式重新进行分配。
curl -XPOST "http://localhost:9200/_cluster/reroute?retry_failed"
awareness allocation decider
可以确保主分片及其副本分片分布在不同的物理服务器,机架或区域之间,以尽可能减少丢失所有分片副本的风险。
filter allocation decider
该 decider 提供了动态参数,可以明确指定分片可以分配到指定节点上。
index.routing.allocation.include.{attribute}index.routing.allocation.require.{attribute}index.routing.allocation.exclude.{attribute}
require 表示必须分配到具有指定 attribute 的节点,include 表示可以分配到具有指定 attribute 的节点,exclude 表示不允许分配到具有指定 attribute 的节点。Elasticsearch 内置了多个 attribute,无需自己定义,包括 _name
, _host_ip
, _publish_ip
, _ip
, _host
。attribute 可以自己定义到 Elasticsearch 的配置文件。
disk threshold allocation decider
根据磁盘空间来控制 shard 的分配,防止节点磁盘写满后,新分片还继续分配到该节点。启用该策略后,它有两个动态参数。
cluster.routing.allocation.disk.watermark.low
参数表示当磁盘空间达到该值后,新的分片不会继续分配到该节点,默认值是磁盘容量的 85%。
cluster.routing.allocation.disk.watermark.high
参数表示当磁盘使用空间达到该值后,集群会尝试将该节点上的分片移动到其他节点,默认值是磁盘容量的 90%。
shards limit allocation decider
通过两个动态参数,控制索引在节点上的分片数量。其中 index.routing.allocation.total _ shards_per_node
控制单个索引在一个节点上的最大分片数;
cluster.routing.allocation.total_shards_per_node
控制一个节点上最多可以分配多少个分片。
应用中为了使索引的分片相对均衡的负载到集群内的节点,index.routing.allocation.total_shards_per_node
参数使用较多。
分片平衡对 Elasticsearch 稳定高效运行至关重要。下面介绍 Elasticsearch 提供的分片平衡参数。
cluster.routing.rebalance.enable
控制是否可以对分片进行平衡,以及对何种类型的分片进行平衡。可取的值包括:all
、primaries
、replicas
、none
,默认值是all
。
all
是可以对所有的分片进行平衡;primaries
表示只能对主分片进行平衡;replicas
表示只能对副本进行平衡;none
表示对任何分片都不能平衡,也就是禁用了平衡功能。该值一般不需要修改。
cluster.routing.allocation.balance.shard
控制各个节点分片数一致的权重,默认值是 0.45f。增大该值,分配 shard 时,Elasticsearch 在不违反 Allocation Decider 的情况下,尽量保证集群各个节点上的分片数是相近的。
cluster.routing.allocation.balance.index
控制单个索引在集群内的平衡权重,默认值是 0.55f。增大该值,分配 shard 时,Elasticsearch 在不违反 Allocation Decider 的情况下,尽量将该索引的分片平均的分布到集群内的节点。
index.routing.allocation.total_shards_per_node
控制单个索引在一个节点上的最大分片数,默认值是不限制。
当使用cluster.routing.allocation.balance.shard
和index.routing.allocation.total_shards_per_node
不能使分片平衡时,就需要通过该参数来控制分片的分布。
所以,我们的经验是:创建索引时,尽量将该值设置的小一些,以使索引的 shard 比较平均的分布到集群内的所有节点。
但是也要使个别节点离线时,分片能分配到在线节点,对于有 10 个几点的集群,如果单个索引的主副本分片总数为 10,如果将该参数设置成 1,当一个节点离线时,集群就无法恢复成 Green 状态了。
所以我们的建议一般是保证一个节点离线后,也可以使集群恢复到 Green 状态。
Elasticsearch 内部的平衡策略都是基于 shard 数量的,所以在运行一段时间后,如果不同索引的 shard 物理大小差距很大,最终会出现磁盘使用不平衡的情况。
所以,目前来说避免该问题的以办法是让集群内的 shard 物理大小尽量保持相近。
主节点对 shard 的管理是一种代价相对比较昂贵的操作,因此在满足需求的情况下建议尽量减少 shard 数量,将分片数量和分片大小控制在合理的范围内,可以避免很多问题。
下一节我们将介绍分片内部的段合并相关问题。
为了方便学习和技术交流,创建了高可用 Elasticsearch 集群的读者群,入群方式在第 1-3 课,欢迎已购课程的同学入群交流。
点击了解《高可用 Elasticsearch 集群 21 讲》。
阅读全文: http://gitbook.cn/gitchat/column/5ce4ff9a308dd66813d92799