本文原文链接
尼恩说在前面
在40岁老架构师 尼恩的读者交流群(50+)中,最近有小伙伴拿到了一线互联网企业如得物、阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试资格,遇到很多很重要的面试题:
- 项目中是如何使用ES(elasticsearch)的?如何优化的?
- 对于 GC 方面,在使用 Elasticsearch 时要注意什么?
- ES 调优的手段有哪些?
最近有小伙伴在面试 jd、字节,又遇到了相关的面试题。小伙伴懵了,因为没有遇到过,所以支支吾吾的说了几句,面试官不满意,面试挂了。
所以,尼恩给大家做一下系统化、体系化的梳理,使得大家内力猛增,可以充分展示一下大家雄厚的 “技术肌肉”,让面试官爱到 “不能自已、口水直流”,然后实现”offer直提”。
当然,这道面试题,以及参考答案,也会收入咱们的 《尼恩Java面试宝典PDF》V171版本,供后面的小伙伴参考,提升大家的 3高 架构、设计、开发水平。
最新《尼恩 架构笔记》《尼恩高并发三部曲》《尼恩Java面试宝典》的PDF,请关注本公众号【技术自由圈】获取,回复:领电子书
来一个大的介绍:ElasticSearch的性能优化的几大方面
总的来说,ElasticSearch的性能优化,可以从以下方面的考虑:
-
硬件层面的优化:
机器分配,机器配置,机器内存,机器CPU,机器网络,机器磁盘性能
-
系统层面的优化:
文件句柄优化、swap关闭
-
ElasticSearch集群层面的优化
合理分配节点,
合理分配参加竞选Master的节点
-
ElasticSearch 索引层面的优化,
副本数量、索引数量、分片数量
-
ElasticSearch查询层面的优化
-
7 职责分离,全面监控
1 硬件层面的优化
1.1 为ES进行 硬件层面的 内存优化:
Elasticsearch是一个内存密集型应用,合理分配内存对于Elasticsearch 提高性能至关重要:
-
确保系统内存有足够的空间用于文件缓存,而不单单是JVM堆内存。
-
建议将一半的内存分配给Elasticsearch的堆内存,另一半留给操作系统和其他进程。
-
为了避免垃圾回收(GC)成为瓶颈,建议设置堆内存的最大值为30GB或更小。可以通过修改
jvm.options文件来调整堆内存的大小。
总之,ES是比较依赖内存的,并且对内存的消耗也很大,内存对ES的重要性甚至是高于CPU的,所以即使是数据量不大的业务,为了保证服务的稳定性,在满足业务需求的前提下,我们仍需考虑留有不少于20%的冗余性能。
根据业务量不同,内存的需求也不同,一般生产建议总体 32内存,其中JVM 不要少于16G, 还有 16G给底层的Luence。
为ES进行 系统层面的 磁盘优化
对于ES来说,磁盘可能是最重要的了,因为数据都是存储在磁盘上的,当然这里说的磁盘指的是磁盘的性能。
磁盘性能往往是硬件性能的瓶颈,木桶效应中的最短板。
方式一:纵向扩展: 使用最高性能的 磁盘
ES 磁盘优化的最为简单,最为粗暴的方式是 纵向扩展,就是使用最高性能的 磁盘,比如SSD 固态硬盘。
ES应用可能要面临不间断的大量的数据读取和写入,数据规模非常庞大, 尼恩曾经架构过的一个30个节点ES集群,数据量非常庞大,达到1TB 。
一般来说,速度最快的,也是最昂贵的。
不可能都用SSD硬盘, 一般是 SSD和机械硬盘组合使用。
所以,可以考虑把节点冷热分离:
-
“热节点”使用SSD做存储,可以大幅提高系统性能;
-
“冷数据“存储在机械硬盘中,降低成本。
-
如果使用磁盘阵列,可以使用RAID 0。
这里,尼恩给大家来点基础知识, 什么是RAID 0?
RAID(Redundant Array of Independent Disks,独立磁盘冗余阵列)是一种将多个物理磁盘组合成一个逻辑单元的数据存储虚拟化技术,其目的是提高数据存储的性能、可靠性或两者兼顾。
RAID 0 是RAID中一种配置方式,具体含义如下:
-
RAID 0 定义:
RAID 0 又称为条带化(Striping),它将数据分散存储在两个或多个硬盘上,每个硬盘只存储数据的一部分,从而提高数据的读写速度。
-
工作原理:
在RAID 0配置中,数据被分成多个块(称为“条带”),并按顺序存储在多个硬盘上。
例如,如果有一个4KB的数据块,它会被分成4个1KB的块,分别存储在4个硬盘上。
读取数据时,因为可以同时从多个硬盘读取,所以读取速度会加快。
写入数据时也是同理,数据会被分成多个块并行写入多个硬盘。
-
性能提升:
RAID 0 的主要优势在于提高数据的读写速度。
由于数据被分散在多个硬盘上,所以理论上,RAID 0 的读写速度可以达到单个硬盘速度的总和。
-
没有冗余:
RAID 0 的缺点是没有数据冗余。如果其中一个硬盘发生故障,整个阵列上的数据都会丢失,因为每个硬盘上只存储数据的一部分,没有副本。
-
适用场景:
RAID 0 适用于对性能要求高而对数据安全性要求不高的场景,如视频编辑、大型数据库的临时存储等。
-
RAID 0 风险:
由于没有数据冗余,RAID 0 的风险较高。
一旦硬盘发生故障,整个RAID 0 阵列上的数据都会丢失,因此在使用RAID 0 时需要非常小心,并且定期备份数据。
总结来说,RAID 0 是一种通过将数据分散存储在多个硬盘上来提高性能的配置方式,但它不提供任何数据冗余,因此使用时需要考虑到数据丢失的风险。
这里,尼恩给大家再来点基础知识, 看看 RAID 0 和 RAID 1 的区别?
RAID 0 和 RAID 1 是两种不同的RAID配置,RAID 0 和 RAID 1 在数据存储、性能和可靠性方面有显著的区别:
-
RAID 0(条带化): 多块磁盘并行读写
数据存储:数据被分割成多个块(条带),然后分散存储在两个或多个硬盘上。这意味着每个硬盘上存储的是数据的不同部分,而不是数据的副本。
性能:由于数据的读写操作可以并行进行,RAID 0 可以提供很高的数据传输速率,特别是在写入大量数据时。
可靠性:RAID 0 没有数据冗余,任何一个硬盘的故障都会导致整个阵列上的数据丢失。因此,RAID 0 的可靠性最低。
适用场景:适合对读写性能要求高、对数据安全性要求不高的应用,如视频处理、大型数据库的临时存储等。
-
RAID 1(镜像): 多块磁盘冗写,并行读
数据存储:数据在两个硬盘上存储完全相同的副本,即镜像。这意味着数据在两个硬盘上都有备份。
性能:RAID 1 的读取性能通常比写入性能要好,因为多个硬盘可以同时读取数据。但是,写入性能可能受到限制,因为数据需要同时写入两个硬盘。
可靠性:RAID 1 提供了很好的数据冗余,即使一个硬盘发生故障,另一个硬盘上的数据副本仍然可以保证数据的完整性和可用性。
适用场景:适合对数据安全性要求高的应用,如关键业务数据存储、服务器操作系统等。
总结来说,RAID 0 提供了高性能但牺牲了数据安全性,而RAID 1 则提供了数据冗余和较高的可靠性,但性能相对较低。在选择RAID配置时,需要根据具体的应用需求和预算来决定使用哪种RAID级别。
方式二:横向扩展:使用多块硬盘提高 硬盘的性能
横向 扩展,就是 搞多块磁盘。
Elasticsearch 通过多个path.data目录配置,把Elasticsearch数据条带化分配到使用多块硬盘上面,以提高I/O性能。
要在Elasticsearch中使用多块硬盘,并通过配置多个path.data目录来实现数据的条带化分配,可以按照以下步骤操作:
- 配置多个数据路径:
在Elasticsearch的配置文件elasticsearch.yml中,可以指定多个数据存储路径(对应到多个磁盘),用逗号分隔。
例如,如果你有三块硬盘,并且已经分别挂载在/mnt/disk1、/mnt/disk2和/mnt/disk3,你可以这样配置:
path.data: /mnt/disk1,/mnt/disk2,/mnt/disk3
这样配置后,Elasticsearch会尝试在这些路径上存储数据,类似于软件层面的RAID 0配置。
- ES的分片分配机制:
Elasticsearch在选择存储分片的路径时,会根据一定的逻辑来选择最佳路径。
Elasticsearch会优先选择磁盘使用率较低(/最为空闲)的磁盘来存储新的分片数据文件。
如果多个路径可用,Elasticsearch会根据剩余空间和已存储的分片数量来决定将分片分配到哪个路径上。
- 数据安全性问题:
虽然使用多个path.data路径可以提高I/O性能,但这种做法类似于软件层面的RAID 0,没有在磁盘层面 做 数据冗余。
如果其中一个硬盘发生故障,存储在该硬盘上的所有分片都会丢失。
因此,官方建议,确保每个分片至少有一个副本分片,以防止数据丢失。
系统层面的ES 磁盘优化 总结:
- ES应用可能要面临不间断的大量的数据读取和写入,磁盘关系到 ES的性能,建议使用最高速的磁盘设备(如SSD)可以显著提高Elasticsearch的性能。
- 当然最高速也是最昂贵的,不可能都用SSD硬盘, 一般是 SSD和机械硬盘组合使用。
- 合理规划磁盘布局,可以考虑把节点冷热分离, 将数据按照冷热的程度,存储在不同的磁盘上,可以避免磁盘I/O竞争。
- 使用RAID 0可以提高磁盘I/O,但要注意数据的安全性。
- 通过多个
path.data目录配置,把Elasticsearch数据条带化分配到使用多块硬盘上面,以提高I/O性能。 - 避免使用远程挂载的存储,比如NFS或者SMB/CIFS,因为这会引入延迟,影响性能。
1.2 为ES进行 硬件层面的 CPU 调优
CPU对其他的 应用程序而言,可谓是最重要的硬件,
但对于ES来说,CPU不是他最依赖的硬件, 提升CPU配置可能不会像提升磁盘或者内存配置带来的性能收益更直接、显著。在成本预算一定的前提下,应该把更多的预算花在磁盘以及内存上面。
当然也不是说CPU的性能就不重要,Elasticsearch在处理查询和索引操作时会消耗大量的CPU资源。
服务器的CPU不需要太高的单核性能,更多的核心数和线程数意味着更高的并发处理能力。现在PC的配置8核都已经普及了,更不用说服务器了。
对于ES来说,通常来说单节点cpu 4核起步,不同角色的节点对CPU的要求也不同。
CPU优化的第一步:角色分离,合理分配节点角色与资源
在 ElasticSearch 集群中,明确划分主节点(Master)、数据节点(Data)和协调节点(Coordinating)的角色。
主节点(Master)主要负责集群的管理和元数据存储,其 CPU 负载主要来自于集群状态的维护和更新。
数据节点(Data)负责存储和索引数据,在数据写入和查询操作时会占用较多 CPU 资源,这是CPU的大户。
协调节点(Coordinating)负责接收用户查询请求并将其分发到合适的数据节点进行处理,也会消耗一定的 CPU 资源用于请求的转发和结果的合并。
例如,在一个大型的ES集群中,大致的CPU配置如下:
-
主节点(Master)配置在性能稳定、CPU 核心数适中(如 4 - 8 核)的服务器上,专门用于处理集群管理任务;
-
数据节点(Data)则根据数据量和查询负载,分配具有较高 CPU 核心数(如 16 - 32 核)的服务器,以满足大量数据的索引和查询操作;
-
协调节点(Coordinating)可以选择 CPU 核心数相对较少(如 8 - 16 核)但网络性能良好的服务器,用于高效地分发和接收查询请求。
CPU优化的第二步:对线程池进行监控和动态调优
ElasticSearch 线程池类型
ElasticSearch 中有多种线程池,如搜索线程池(search)、索引线程池(index)、批量线程池(bulk)等。
每个线程池负责不同类型的操作,并且其大小会影响 CPU 的利用率。例如:
- 搜索线程池(
search)大小决定了可以同时处理的搜索查询数量 - 索引线程池(
index)大小则影响数据索引的并发处理能力。
优化线程池大小
根据 CPU 核心数和集群的实际负载情况,合理调整线程池大小。
一般来说,线程池大小不应超过 CPU 核心数的两倍,以避免过多的线程竞争 CPU 资源导致性能下降。
例如,在一个具有 16 核 CPU 的数据节点上,可以将搜索线程池大小设置为 30 左右,索引线程池大小设置为 20 左右。
同时,可以通过监控工具(如 ElasticSearch 自带的监控 API 或者第三方监控工具)观察线程池的活跃线程数、队列长度等指标,根据这些指标动态调整线程池大小。
使用 ElasticSearch 自带监控 API获取监控数据
ElasticSearch 提供了丰富的 REST API 用于监控集群状态。可以通过访问
_cat/thread_pool
端点来获取线程池相关的信息。例如,发送一个 HTTP GET 请求到
http://<es-node-ip>:9200/_cat/thread_pool?v
v参数用于以详细格式返回结果),会返回类似如下内容:
pool_name active queue rejected
bulk 0 0 0
fetch 0 0 0
flush 0 0 0
generic 0 0 0
get 0 0 0
index 0 0 0
listener 0 0 0
management 0 0 0
percolate 0 0 0
refresh 0 0 0
search 0 0 0
snapshot 0 0 0
suggest 0 0 0
war m 0 0 0
write 0 0 0
其中,
active列表示当前活跃的线程数,queue列表示线程池队列中的任务数,rejected列表示被拒绝的任务数。
通过这些核心指标,可以解每个线程池的负载情况。
动态调整线程池大小(通过 API 或配置文件)
如果发现 线程池负载高,或者 `queue 线程池队列中的任务数 积压多,可以进行动态调整线程池大小调整
在 ElasticSearch 中,可以通过更新集群设置来动态调整线程池大小,可以使用下面的API,
_cluster/settings
发送一个 PUT类型的settings 请求,例如:
{
"persistent": {
"thread_pool.search.size": 30
}
}
这个请求将搜索线程池(search)的大小调整为 30。
需要注意的是,这种调整方式需要谨慎操作,并且最好在低峰期进行,因为不当的调整可能会对集群性能产生暂时的负面影响。
另外,也可以通过修改elasticsearch.yml配置文件来调整线程池大小,不过这种方式需要重启集群才能生效。
CPU 的优化建议:
-
确保服务器有足够的CPU核心,并合理调整Elasticsearch的线程池配置,可以提高系统的并发处理能力。
-
推荐使用多核处理器(至少8核及以上)来提升处理能力。
-
避免虚拟化过度使用,特别是在生产环境中,因为这可能会引入不必要的性能开销。
-
对线程池进行监控和动态调优, 防止线程任务积压
1.3 为ES进行 硬件层面的 网络优化:
建议为ES 使用 低延迟的网络 :
ES是天生自带分布式属性的,并且ES的分布式系统是基于对等网络的,节点与节点之间的通信十分的频繁,延迟对于ES的用户体验是致命的,所以对于ES来说,低延迟的网络是非常有必要的。
高延迟的,跨多个数据中心的ES集群方案,是不太可取的,
虽然,ES可以容忍集群跨多个机房,可以有多个内网环境,支持跨AZ部署,但是不建议多个机房跨地域构建集群,一旦发生了网络故障,集群可能直接雪崩,即使能够保证服务正常运行,维护这样(跨地域单个集群)的集群带来的额外成本可能远小于它带来的额外收益。
2 系统层面的优化
2.1 为ES进行 系统层面的 文件句柄数 调优
修改ES启动用户可使用的系统文件句柄数,以适应Elasticsearch的需求。
Elasticsearch 需要大量 的文件句柄数, 主要 两大 原因:
第一:ES集群有大量的TCP /http 连接需求
大量的节点间通信连接:
Elasticsearch 集群中节点之间, 需要进行频繁的通信。
如数据同步、状态协调等,这会建立大量的 TCP 连接,每个 TCP 连接都需要占用一个文件句柄1。
ES集群有大量的客户端连接:
当有大量的 HTTP 客户端连接到 Elasticsearch 集群进行数据读写操作时,也会消耗大量的文件句柄。
如果文件句柄数不足,可能导致新的连接无法建立,客户端出现连接超时等问题 。
第二:ES集群有大量的索引与数据操作
ES集群有大量的索引文件操作:
Elasticsearch 在处理索引时,会涉及到大量的文件操作。
例如,在索引创建、更新、删除过程中,需要打开和操作多个索引文件,包括索引元数据文件、数据文件、段文件等。
每个文件都需要一个文件句柄,如果文件句柄数受限,可能会导致索引操作失败或异常。
ES集群有大量的数据段合并:
为了提高查询性能,Elasticsearch 会在后台进行数据段合并操作。
在合并过程中,需要同时打开多个段文件进行读写,如果文件句柄数不足,会影响段合并的效率,甚至导致合并失败 。
问题:文件句柄数的 系统限制,满足不了es的句柄需求
系统默认文件句柄数限制过低:
许多 Linux 发行版默认每个进程允许的文件句柄数通常只有 1024 或更低,这对于处理大量数据和高并发请求的 Elasticsearch 来说远远不够。
避免资源耗尽风险:
如果不调整文件句柄数,当 Elasticsearch 的文件句柄使用达到系统默认限制时,继续请求打开文件就会失败,可能导致数据丢失、节点故障等严重问题 。
调优:如何解除 文件句柄数的 系统限制?
step1:查看当前系统文件句柄数限制:
可以使用ulimit -n命令查看当前文件描述符限制,默认通常是 1024,对于 ES 来说远远不够。
step2:修改文件句柄数
临时修改文件句柄数:
可以 通过ulimit -n 65536命令将当前会话的文件描述符限制临时设置为 65536 。
永久修改文件句柄数:
使用vim /etc/security/limits.conf命令编辑该limits.conf 文件,添加或修改以下配置项:
* soft nofile 65536
* hard nofile 65536
上述配置中,*表示匹配所有用户,soft为警告值,hard为最大值。
检查配置是否生效:重新登录系统后,使用ulimit -n命令查看文件句柄数限制是否已生效
2.2 为ES进行 系统层面的 swap 调优
这里就是简单粗暴,关掉swap。
内存交换 到磁盘对服务器性能来说是 致命 的。
如果内存交换到磁盘上,性能就会急剧下降, 一个 100 微秒的操作可能变成 10 毫秒,下降100倍以上。想想那么多 10 微秒的操作时延累加起来。
不难看出 swapping 对于性能是多么可怕。
所以,Elasticsearch建议禁用Swap分区,因为当物理内存不足时,操作系统会将一些内存页交换到磁盘上,这会导致性能急剧下降。
在Linux系统中, 用以下命令关掉swap:
sudo swapoff -a
在Linux系统中使用ES,最好可以通过修改/etc/sysctl.conf文件来禁用Swap分区:
# 在文件末尾添加以下行
vm.swappiness=1
然后运行sudo sysctl -p使配置生效。
除了 关掉swap ,还可以 使用 memlock ,为ES 锁定的物理内存。
memlock 是一个与内存锁定相关的系统资源限制。它用于指定一个进程能够锁定的物理内存(RAM)的大小。内存锁定意味着将进程使用的内存固定在物理内存中,防止操作系统将其交换(swap)到磁盘上的虚拟内存(磁盘交换空间)。
对于像 Elasticsearch 这样的应用程序,将内存锁定是很重要的。
因为 Elasticsearch 大量使用内存来缓存索引数据,如果这些数据被交换到磁盘,会导致查询性能大幅下降。内存锁定可以确保关键的内存区域(如索引缓存)始终在物理内存中,提高数据访问速度。
可以通过设置Elasticsearch的bootstrap.memory_lock选项来尝试锁定JVM内存,防止其被交换到磁盘上, 在 配置文件elasticsearch.yml中添加以下行
bootstrap.memory_lock: true
注意:这需要用户 有memlock权限。
可以通过ulimit -l命令查看当前用户的memlock限制,并通过ulimit -l unlimited命令设置无限制(但这通常需要root权限)。
在生产环境中,更推荐的方式是通过修改/etc/security/limits.conf文件来永久设置 memlock 限制。
通过修改/etc/security/limits.conf文件来永久设置memlock限制,可按以下步骤进行操作:
用文本编辑器打开/etc/security/limits.conf文件, 在文件末尾添加或修改以下内容 :
* soft memlock [期望的软限制大小]
* hard memlock [期望的硬限制大小]
例如,如果想将memlock限制设置为 8GB(以字节为单位),则可写成:
* soft memlock 8388608
* hard memlock 8388608
这里的*表示匹配所有用户,你也可以根据实际情况指定具体的用户,如elastic soft memlock 8388608和elastic hard memlock 8388608,只对elastic用户生效 。
3 JVM层面的优化
3.1 为ES进行 JVM层面的 JVM堆大小调优
Elasticsearch是运行在JVM上的,对其做JVM参数调优至关重要。
最常见的调优是Java内存的分配。
下面是JVM的内存模型,具体每块的作用,不在这里阐述。

新生代和老年代分配的内存比例给多大?
Jvm内存分为新生代和老年代。
-
新生代(或者伊甸园)
新实例化的对象分配的空间。
新生代空间通常都非常小,一般在 100 MB–500 MB。
新生代也包含两个 幸存 空间。
-
老年代
较老的对象存储的空间。
这些对象预计将长期留存并持续上很长一段时间。
老生代通常比新生代大很多。
新生代、老生代的垃圾回收都有一个阶段会“stop the world”。给新生代和老年代分配多大的内存呢?他们的比例是多少呢?
一般来说,老年代和新生代的内存比例为2:1是比较合适的。
比如给堆内存分配3G,则新生代分配1G,其余都给老年代。
在ElasticSearce的配置文件jvm.options文件配置:
-Xms3g //配置堆初始化大小
-Xmx3g //配置堆的最大内存
-Xmn1g //配置新生代内存。
一个Elasticesearch节点的两个内存大户
内存对于 Elasticsearch 来说绝对是重要的,它可以被许多内存数据结构使用来提供更快的操作。
一台机器装一个Elasticesearch节点,我们应该怎么分配机器的内存呢?
官方给出了解决方案,把一半(少于)的内存分配给Luence,另外的内存分配给

最低0.47元/天 解锁文章

被折叠的 条评论
为什么被折叠?



