暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

如何构建高性能的Elasticsearch集群

Share and Fun喜来分 2019-10-11
403


Elasticsearch 是一种流行的开源搜索引擎,用于实时分布式搜索和数据分析。在产品环境中,Elasticsearch 应该作为集群跨多个服务器部署,以实现最佳性能,稳定性和可伸缩性。同时 Elasticsearch是一个非常通用的平台,它支持各种场景用例,并在数据组织和复制策略方面提供了极大的灵活性。 


但是,这种灵活性有时会使得很难预先确定如何将数据最好地组织为索引和分片,特别是如果你是 Elasticsearch 的新手。如果未能做出最佳规划设计,尽管这在开始的时候可能不会造成问题,但随着数据量越来越大,便有可能会引发性能问题。集群中的数据越多,要纠正这一问题就越难,这是因为有时必须对大量数据进行重新索引。


没有合理的规划和不恰当的配置会导致集群的性能和稳定性大打折扣,本文重点讲解如何构建一个高性能的、高稳定的 Elasticsearch 集群。


集群规划

l  节点数量和类型

Elasticsearch 的节点类型有五种:


主节点(Master Node)

将 node.master 设置为 true 的节点,这使其有资格被选作控制群集的主节点。它主要用于执行轻量的集群操作,例如:创建或删除索引,跟踪节点,决定分片分发到哪个节点等。


数据节点(Data Node)

将 node.data 设置为 true 的节点。数据节点保存数据并执行与数据相关的操作,例如增删改查,搜索和聚合。


接收节点(Ingest Node)

将 node.ingest 设置为 true 的节点。接收外部的数据输入,在建立索引之前进行转换和丰富文档。


协调节点(Coordinating Node)

协调不具备处理主节点的职责,数据节点的数据处理能力和接收节点的预处理文档的能力,它只是路由请求和分配批量索引的协调节点。本质上,协调节点就是简单地充当智能负载均衡器。


机器学习节点(Machine Learning Node)

将 xpack.ml.enabled 和 node.ml 设置为 true 的节点。如果要使用机器学习功能,则需要购买 X-Pack 的许可并且集群中必须至少有一个机器学习节点。


最佳实践:


对于处理数据量不大的小集群,搭建三个节点(即履行所有角色)的集群就挺不错。但是对于较大的集群,请划分出三个专用的主节点。


专用主节点执行集群管理任务,但不保存数据或响应数据输入请求。减轻集群管理任务的负担,可以提高集群的稳定性。


推荐用三个专用主节点,这样可在主节点发生故障时提供两个备用节点,并提供必要的仲裁来选举新的主节点。尽管专用主节点不处理搜索和查询请求,但它们用什么样的实例与它们可以管理的节点数量,索引和分片的数量高度相关。 


对于生产集群,建议专用主节点使用以下实例类型。这些建议基于典型的工作负载,并且会根据你的需求而有所不同。


节点数量

推荐的主节点EC2类型

1-10

c5.large

10-30

c5.xlarge

30-75

c5.2xlarge

75-200

r5.4xlarge


如果您有 10 个以上的节点,需要支持相当大的批量数据接收,并且写入速度很慢,请考虑启用一些专用的接收节点。同样,对于 10 个以上的节点和繁重的并发查询负载,请考虑启用几个专用的协调节点可以提高性能。


l  索引,分片和副本数


Elasticsearch 中的数据会整理为索引(index)。每个索引又由一个或多个分片(shard)组成。每个分片都是一个 Lucene 索引实例,可以将其视作一个独立的搜索引擎,它能够对 Elasticsearch 集群中的数据子集进行索引并处理相关查询。


随着段数越来越多,这些段(segment)会定期合并为更大的段。这一过程称为合并。由于所有段都是不可更改的,这意味着在索引期间所用磁盘空间通常会上下浮动,这是因为只有合并后的新段创建完毕之后,它们所替换的那些段才能删掉。合并是一项极其耗费资源的任务,尤其耗费磁盘 I/O。


分片是 Elasticsearch 在集群内分发数据的单位。Elasticsearch 在对数据进行再平衡(例如发生故障后)时移动分片的速度取决于分片的大小和数量,以及网络和磁盘性能。


每个分片都有一部分数据需要保存在内存中,这部分数据也会占用堆内存空间。这包括存储分片级别以及段级别信息的数据结构,因为只有这样才能确定数据在磁盘上的存储位置。这些数据结构的大小并不固定,不同用例之间会有很大的差别。


段相关开销有一个重要特征,那就是其并不与段的大小呈严格正比关系。这意味着,与较小的段相比,对于较大的段而言,其单位数据量所需的开销要小一些。


为了能够在单个节点上存储尽可能多的数据,下面两点至关重要:管理堆内存使用量;尽可能减少开销。节点的堆内存空间越多,其能处理的数据和分片就越多。


最佳实践:


每个节点上可以存储的分片数量与可用的堆内存大小成正比关系,大概的经验法则:确保对于节点上已配置的每个 GB,将分片数量保持在 20 以下。


假设用数据节点的机器类型为 m5.2xlarge(8 vCPU, 32G 内存),那么最多可以为数据节点设置 16GB 的堆内存,这样一个数据节点最多可以有 320 个分片。


分片过小会导致段过小,进而致使开销增加;分片过大,会对集群从故障中恢复造成不利影响。要尽量将分片的平均大小控制在至少几 GB 到几十 GB 之间。


对于 100G 一下的 index,设置 3-5 个分片,对于 100G 以上的 index,分片大小通常控制在 20GB 至 50GB 之间。基于这个准则,使用时序型索引来存储固定期限内的数据时,应根据保留期和预计数据量来按日、周、双周或月来建索引,从而确保达到目标分片大小。


每个 index 的副本数量过多会导致存储成本增加,过少会导致查询性能较差,所以对于查询压力较大的index,通过增加副本数来均摊查询压力。


根据索引数,每个索引的分片数以及每个分片副本数算出总的分片数,最后可以预算出所需要的数据节点数。假设有 200 个索引,每个索引有 5 个分片,每个分片有一个副本,那么总的分片数为 200*5*2=2000,那么就需要大概 7 个 m5.2xlarge 的数据节点。


操作系统配置优化

l  内存交换

内存交换(swapping)是这样一个过程,它把内存分页复制到预先设定的叫做交换区的硬盘空间上,以此释放内存分页。物理内存和交换区加起来的大小就是虚拟内存的可用额度。


内存交换对 Elasticsearch 的性能有非常大的影响, 所以我们需要禁止内存交换。通常有两个方法:


1. 在Lunix系统中,在文件/etc/fstab文件中注释掉所有包含swap的行,或者执行下面的命令临时禁止内存交换。

sudo swapoff -a


2. 在config/Elasticsearch.yml文件中,加上下面一行。

bootstrap.memory_lock: true


最大打开文件数及最大文件大小

Elasticsearch 需要用到很多文件句柄,耗尽文件句柄可能导致数据丢失。确保最大的打开文件数设置为65535 或以上。在 Linux 系统中,可以在 /etc/security/limits.conf 文件中添加下面一行配置。

elasticsearch  -  nofile  65535
elasticsearch  -  fsize  unlimited


最大进程数

Elasticsearch 针对不同的操作使用很多线程池,确保可以创建的线程数最少为 4096。在 Linux 系统中,可以在 /etc/security/limits.conf 文件中添加下面一行配置。

elasticsearch  -  nproc  4096


Elasticsearch配置优化

l  内存设置

Elasticsearch启动时应该设置多少的内存呢?比如机器有 64G 内存,那么我们是不是设置的越大越好呢?


其实不是的。主要 Elasticsearch 底层使用 Lucene。Lucene 被设计为可以利用操作系统底层机制来缓存内存数据结构。Lucene 的段是分别存储到单个文件中的。因为段是不可变的,这些文件也都不会变化,这是对缓存友好的,同时操作系统也会把这些段文件缓存起来,以便更快的访问。


如果你把所有的内存都分配给 Elasticsearch 的堆内存,那将不会有剩余的内存交给 Lucene。这将严重地影响全文检索的性能。通常是把 50% 的可用内存作为 Elasticsearch 的堆内存,保留剩下的 50%的内存给Lucene 使用。


同时了解过 Elasticsearch 的同学都听过”不要超过 32G ”的说法吧。通常为 CPU 字长的大小:32 位或 64 位,取决于你的处理器。指针引用的就是对象的字节位置。对于 32 位的系统,意味着堆内存大小最大为 4 GB。对于 64 位的系统,可以使用更大的内存,但是 64 位的指针意味着更大的浪费,因为你的指针本身大了。更糟糕的是, 更大的指针在主内存和各级缓存(例如 LLC,L1 等)之间移动数据的时候,会占用更多的带宽。


java 使用一个叫内存指针压缩的技术来解决这个问题。它的指针不再表示对象在内存中的精确位置,而是表示偏移量。这意味着32位的指针可以引用 40 亿个对象,而不是 40 亿个字节。最终,也就是说堆内存达到 32G 时,也可以用 32bit 的指针表示。但是实际设置时,应该略低于 jvm 零基指针压缩的极限值,确切的值要根据 JVMs 和操作系统而定。


如果想保证其安全可靠,可 JVM 设置里添加 -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompressedOopsMode 来打印相应的 log 来判断就是用的零基指针压缩还是普通指针压缩。经验值是在 26G 到 30G 之间。


l  Refresh 时间间隔

为了提高索引性能,Elasticsearch 在写入数据时,采用延迟写入的策略,即数据先写到内存中,当超过默认 1 秒(index.refresh_interval)会进行一次写入操作,就是将内存中小段合并成大段写入磁盘,此时我们才能将数据搜索出来,所以这就是为什么Elasticsearch 提供的是近实时搜索功能,而不是实时搜索功能。


当然对数据延迟要求不高的系统,我们可以通过延长 refresh 时间间隔,可以有效的减少段合并压力,提高索引速度。在做日志收集系统时,我们就将 index.refresh_interval 设置为15s,减少 refresh 次数。


l  数据冷热分离,读写分离

从节省成本的角度,大集群的索引数据可以做冷热分离,读写分离。热数据存储用 SSD 和普通历史数据存储用机械磁盘。比如最近一个月的数据存储在热节点,一个月前的数据通过后台进程迁移到冷节点。


l  按照日期规划索引

随着业务量的增加,单一索引的数据量随着时间不断增长,按照日期规划索引是必然选择。这样可以实现历史数据秒删,同时也便于冷热数据分开管理,检索最近几天的数据,直接指定对应日期的索引,速度非常快!


l  数据模型

对应一些没有什么意义的字段,尽量避免输入到索引,可以考虑在 logstash 或者 filebeats 中过滤掉。


不要使用默认的 Mapping,默认 Mapping 的字段类型是系统自动识别的。其中:string 类型默认分成 text 和 keyword 两种类型。如果你的业务中不需要分词、检索,仅需要精确匹配,仅设置为 keyword 即可。


l  节流控制

Elasticsearch 本身不能做节流控制也没有反压机制,如果大量突发消息涌入集群,可能会导致集群崩溃。所以在输入端需要考虑流控,比如输入端是 logstash 的话,可以用 logstash 的 throttle 加上 sleep 插件做流控。通用的做法引入消息中间件,比如 Kafka 加上 Kafka Connector。


l  搜索优化

尽量避免 wildcard 模糊匹配,数据量级达到 TB+ 更高之后,wildcard 在多字段组合的情况下很容易出现卡死,甚至导致集群节点崩溃宕机的情况。


大量使用 filter 过滤器,无 疑filter 机制会使得检索更快。


和 mysql 查询一样,业务开发中,select * 操作几乎是不必须的。同理,ES 中,_source 返回全部字段也是非必须的。要通过 _source 控制字段的返回,只返回业务相关的字段。


结论

正由于 Elasticsearch 非常的灵活,如何最好地配置和优化 ES 集群,很多决策仍取决于应用场景的具体情况,有时的确很难确定如何最好地应用上面的建议,所以需要用实际的数据去做相应的性能测试,然后选择最佳性能的配置。


文章转载自Share and Fun喜来分,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论