来一个大的介绍:ElasticSearch的性能优化的几大方面

本文借鉴尼恩博主,参考学习

总的来说,ElasticSearch的性能优化,可以从以下方面的考虑:
  • 硬件层面的优化:
    机器分配,机器配置,机器内存,机器CPU,机器网络,机器磁盘性能
  • 系统层面的优化:
    文件句柄优化、swap关闭
  • ElasticSearch集群层面的优化 重点
    合理分配节点,
    合理分配参加竞选Master的节点
  • ElasticSearch 索引层面的优化 重点
    副本数量、索引数量、分片数量
  • ElasticSearch查询层面的优化
  • 职责分离,全面监控

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中一种配置方式,具体含义如下:

  1. RAID 0 定义
    RAID 0 又称为条带化(Striping),它将数据分散存储在两个或多个硬盘上,每个硬盘只存储数据的一部分,从而提高数据的读写速度。
  2. 工作原理
    在RAID 0配置中,数据被分成多个块(称为“条带”),并按顺序存储在多个硬盘上。
    例如,如果有一个4KB的数据块,它会被分成4个1KB的块,分别存储在4个硬盘上。
    读取数据时,因为可以同时从多个硬盘读取,所以读取速度会加快。
    写入数据时也是同理,数据会被分成多个块并行写入多个硬盘。
  3. 性能提升
    RAID 0 的主要优势在于提高数据的读写速度。
    由于数据被分散在多个硬盘上,所以理论上,RAID 0 的读写速度可以达到单个硬盘速度的总和。
  4. 没有冗余
    RAID 0 的缺点是没有数据冗余。如果其中一个硬盘发生故障,整个阵列上的数据都会丢失,因为每个硬盘上只存储数据的一部分,没有副本。
  5. 适用场景
    RAID 0 适用于对性能要求高而对数据安全性要求不高的场景,如视频编辑、大型数据库的临时存储等。
  6. RAID 0 风险
    由于没有数据冗余,RAID 0 的风险较高。
    一旦硬盘发生故障,整个RAID 0 阵列上的数据都会丢失,因此在使用RAID 0 时需要非常小心,并且定期备份数据。

总结来说,RAID 0 是一种通过将数据分散存储在多个硬盘上来提高性能的配置方式,但它不提供任何数据冗余,因此使用时需要考虑到数据丢失的风险。

这里,尼恩给大家再来点基础知识, 看看 RAID 0 和 RAID 1 的区别?

RAID 0 和 RAID 1 是两种不同的RAID配置,RAID 0 和 RAID 1 在数据存储、性能和可靠性方面有显著的区别:

  1. RAID 0(条带化): 多块磁盘并行读写
    数据存储:数据被分割成多个块(条带),然后分散存储在两个或多个硬盘上。这意味着每个硬盘上存储的是数据的不同部分,而不是数据的副本。
    性能:由于数据的读写操作可以并行进行,RAID 0 可以提供很高的数据传输速率,特别是在写入大量数据时。
    可靠性:RAID 0 没有数据冗余,任何一个硬盘的故障都会导致整个阵列上的数据丢失。因此,RAID 0 的可靠性最低。
    适用场景:适合对读写性能要求高、对数据安全性要求不高的应用,如视频处理、大型数据库的临时存储等。
  2. RAID 1(镜像): 多块磁盘冗写,并行读
    数据存储:数据在两个硬盘上存储完全相同的副本,即镜像。这意味着数据在两个硬盘上都有备份。
    性能:RAID 1 的读取性能通常比写入性能要好,因为多个硬盘可以同时读取数据。但是,写入性能可能受到限制,因为数据需要同时写入两个硬盘。
    可靠性:RAID 1 提供了很好的数据冗余,即使一个硬盘发生故障,另一个硬盘上的数据副本仍然可以保证数据的完整性和可用性。
    适用场景:适合对数据安全性要求高的应用,如关键业务数据存储、服务器操作系统等。

总结来说,RAID 0 提供了高性能但牺牲了数据安全性,而RAID 1 则提供了数据冗余和较高的可靠性,但性能相对较低。在选择RAID配置时,需要根据具体的应用需求和预算来决定使用哪种RAID级别。

方式二:横向扩展:使用多块硬盘提高 硬盘的性能

横向 扩展,就是 搞多块磁盘。

Elasticsearch 通过多个path.data目录配置,把Elasticsearch数据条带化分配到使用多块硬盘上面,以提高I/O性能。

要在Elasticsearch中使用多块硬盘,并通过配置多个path.data目录来实现数据的条带化分配,可以按照以下步骤操作:

  1. 配置多个数据路径

在Elasticsearch的配置文件elasticsearch.yml中,可以指定多个数据存储路径(对应到多个磁盘),用逗号分隔。
例如,如果你有三块硬盘,并且已经分别挂载在/mnt/disk1/mnt/disk2/mnt/disk3,你可以这样配置:

   path.data: /mnt/disk1,/mnt/disk2,/mnt/disk3

这样配置后,Elasticsearch会尝试在这些路径上存储数据,类似于软件层面的RAID 0配置。

  1. ES的分片分配机制

Elasticsearch在选择存储分片的路径时,会根据一定的逻辑来选择最佳路径。

Elasticsearch会优先选择磁盘使用率较低(/最为空闲)的磁盘来存储新的分片数据文件。

如果多个路径可用,Elasticsearch会根据剩余空间和已存储的分片数量来决定将分片分配到哪个路径上。

  1. 数据安全性问题

虽然使用多个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 8388608elastic hard memlock 8388608,只对elastic用户生效 。

3 JVM层面的优化

3.1 为ES进行 JVM层面的 JVM堆大小调优

Elasticsearch是运行在JVM上的,对其做JVM参数调优至关重要。

最常见的调优是Java内存的分配。

下面是JVM的内存模型,具体每块的作用,不在这里阐述。

image.png

新生代和老年代分配的内存比例给多大?

Jvm内存分为新生代和老年代。

  • 新生代(或者伊甸园)
    新实例化的对象分配的空间。
    新生代空间通常都非常小,一般在 100 MB–500 MB。
    新生代也包含两个 幸存 空间。
  • 老年代
    较老的对象存储的空间。
    这些对象预计将长期留存并持续上很长一段时间。
    老生代通常比新生代大很多。

新生代、老生代的垃圾回收都有一个阶段会“stop the world”。给新生代和老年代分配多大的内存呢?他们的比例是多少呢?

一般来说,老年代和新生代的内存比例为2:1是比较合适的。

比如给堆内存分配3G,则新生代分配1G,其余都给老年代。

在ElasticSearce的配置文件jvm.options文件配置:

-Xms3g  //配置堆初始化大小
-Xmx3g   //配置堆的最大内存
-Xmn1g   //配置新生代内存。

一个Elasticesearch节点的两个内存大户

内存对于 Elasticsearch 来说绝对是重要的,它可以被许多内存数据结构使用来提供更快的操作。

一台机器装一个Elasticesearch节点,我们应该怎么分配机器的内存呢?

官方给出了解决方案,把一半(少于)的内存分配给Luence,另外的内存分配给ElasticSearch.

是一个Elasticesearch内存大户,底层的 Luence 另一个内存大户:

第一个 内存大户Elasticsearch 缓存

(1) 文档 缓存数据

最重要的是缓存索引数据。

首先 是 文档数据缓存。 当执行查询操作时,经常访问的文档数据会被缓存在堆内存中。

例如:一个电商商品搜索系统使用 Elasticsearch,用户频繁搜索的热门商品信息(如商品名称、价格、品牌等)会被缓存,这样在后续查询时可以快速从内存中获取,减少磁盘 I/O 操作,提高查询响应速度。

其次 是过滤器缓存, 也存储在堆内存中。

当使用过滤器进行查询(如筛选出特定品牌的商品)时,过滤器的结果会被缓存,下次遇到相同的过滤条件时可以直接利用缓存结果,提高过滤效率。

(2) 索引缓冲区

在数据写入索引的过程中,会有一个索引缓冲区(Index Buffer)在堆内存中。

新的数据首先进入这个缓冲区,当缓冲区满或者达到一定的阈值时,数据会被刷新到磁盘上的索引文件中。

(3) 查询相关数据结构

存储查询执行过程中的临时数据结构,如用于排序的字段值集合。

例如,对商品按照价格进行排序的查询,排序过程中的价格数据会暂存在堆内存中。

还有聚合操作的中间结果,比如在统计不同品牌商品数量的聚合查询中,每个品牌的计数中间结果会存储在堆内存。

(4) 集群状态 缓存

Elasticsearch 主节点需要在堆内存中存储集群状态信息,包括索引元数据(如索引名称、分片数量、副本数量等)、节点信息(如节点角色、节点健康状态等)。

这些信息对于集群的管理和协调至关重要,如在进行分片分配、故障转移等操作时会使用到这些信息。

第二个 内存大户 Lucene 非堆内存(off - heap)

(1) 索引文件数据

Lucene 是 Elasticsearch 的底层搜索引擎库,大部分索引数据实际上存储在非堆内存(磁盘文件)中。

索引文件包含了倒排索引,倒排索引是 Lucene 的核心数据结构。

例如,对于文档中的每个单词,倒排索引记录了包含该单词的文档列表。

在文本搜索场景下,如新闻网站的文章搜索,文章中的词汇和对应的文档编号存储在非堆内存的索引文件中,用于快速定位包含特定词汇的文章。

(2) 段文件(Segment Files)存储在非堆内存中。

Lucene 会将索引划分为多个段,每个段是一个独立的索引部分。

这些段文件包含了文档数据、词向量等信息,并且会根据一定的策略进行合并和优化。

例如,在数据更新过程中,新的数据可能会形成新的段,然后通过段合并操作来优化索引结构,这些段文件存储在非堆内存中。

(3)文件系统缓存

操作系统会对磁盘上的索引文件进行缓存,这部分缓存数据虽然不在 Java 堆内存中, 也是(off - heap)非堆内存的一部分。

当 Lucene 读取索引文件时,操作系统会将最近访问的文件块缓存在内存中,提高文件的访问速度。

例如,频繁被查询的索引段文件部分会被操作系统缓存,下次访问时可以直接从缓存中读取,减少磁盘读取时间。

Lucene 使用 内存映射文件(Memory - Mapped Files)数据,将磁盘上的索引文件映射到内存中,使得文件访问就像访问内存一样高效。

这部分内存映射的数据也属于非堆内存。通过这种方式,Lucene 可以直接在内存中操作索引文件的部分内容,而不需要进行大量的文件 I/O 操作,提高了索引数据的读写效率。

该分配多大的内存给Elasticesearch?

  • 如果 把所有的内存都分配给 Elasticsearch 的堆内存,那将不会有剩余的内存交给 Lucene。这将严重地影响全文检索的性能。
  • 标准的建议是把 50% 的可用内存作为 Elasticsearch 的堆内存,保留剩下的 50%,Lucene 会很乐意利用起余下的内存,也就是把 50% 的可用内存作为 Lucene 的非堆内存(off - heap)。
  • Lucene 被设计为可以利用操作系统底层机制来缓存内存数据结构,所以这部分Lucene 的非堆内存(off - heap),预留就OK了,不需要专门设置。

所以,在使用Elasticesearch的时候,按照实际的系统内存大小,一般进行Elasticesearch堆大小进行对应的配置,从最小规格的8G(如果 os内存 16g) 到了中等规模16G内存(如果 os内存 32g),然后最大规格32G内存(如果 os内存>64g)。

分配的建议如下:

  • 如果 os内存 16g ,jvm 8G
  • 如果 os内存 32g ,jvm 16g
  • 如果 os内存>64g ,jvm 32g

Xms和Xmx设置:

合理设置堆内存大小, 一般建议将堆内存设置为物理内存的 50 - 70%。

通过-Xms(初始堆大小)和-Xmx(最大堆大小)参数来设置初始堆大小、最大堆大小 相同。例如:

-Xms30g
-Xmx30g

确保-Xms-Xmx的值相同,这样可以避免在堆内存扩展或收缩过程中产生性能开销。

建议将JVM堆内存的初始大小(-Xms)和最大大小(-Xmx)设置为相同的值,以避免运行过程中动态调整堆内存大小,这是一个损耗性能的操作。

可以通过修改

jvm.options

文件来调整堆内存的大小。例如,将堆内存设置为16GB的配置如下:

-Xms16g
-Xmx16g

这里将堆内存的初始大小和最大大小都设置为16GB,你可以根据实际情况调整

分配给Elasticesearch 最大内存应该小于 32766 mb(\~31.99 gb)

为啥呢? JVM 在内存小于 32 GB 的时候会采用一个内存对象指针压缩技术。

  • 对于 32 位的系统,意味着堆内存大小最大为 4 GB。
  • 对于 64 位的系统, 可以使用更大的内存,但是 64 位的指针意味着更大的浪费,因为你的指针本身大了。

更糟糕的是, 更大的指针在主内存和各级缓存(例如 LLC,L1 等)之间移动数据的时候,会占用更多的带宽。

所以, 为了节省 64 位的指针 占用内存的大小, Java 使用一个叫作 内存指针压缩(compressed oops)的技术来解决这个问题。

使用 内存指针压缩(compressed oops)的技术, 对象的指针不再表示对象在内存中的精确位置,而是表示 偏移量 。

这意味着 32 位的指针可以引用 40 亿个 对象 , 而不是 40 亿个字节。

从极端角度来说,假设一个对象一个字节,使用 内存指针压缩(compressed oops)的技术后, , 32 位的指针表示对象的指针理解为,最大增长到 32 GB 的物理内存。

一旦你越过那个神奇的 \~32 GB 的边界,指针就会切回普通对象的指针。

切回普通对象的64位指针,每个对象的指针都变长了,就会使用更多的 CPU 内存带宽,也就是说你实际上失去了更多的内存。

事实上,哪怕是 40–50 GB 内存,和使用了内存对象指针压缩技术时候的 32 GB 内存的 效果是一样的, 10多个G白白的给普通对象的64位指针了 。

总之:即便你有足够的内存,JVM 也尽量不要 超过 32 GB。因为它浪费了内存,降低了 CPU 的性能,还要让 GC 应对大内存。

3.2 垃圾回收器的优化

3.2.1:JDK8 + G1 垃圾回收器

很多公司生产环境使用的是老的es 6 系列版本(比较稳定),如

  • JDK:JDK1.8\_171-b11 (64位)
  • ES集群:一般由3台16核32G的虚拟机部署 ES 集群,每个节点分配16G堆内存
  • ELK版本:6.3.0
  • 垃圾回收器:ES 默认指定的老年代(CMS)+ 新生代(ParNew)
  • 操作系统:CentOS Linux release 7.4.1708(Core)

Elasticsearch 默认的垃圾回收器( GC )是 CMS。官方建议使用 CMS。

这个垃圾回收器可以和应用并行处理,以便它可以最小化停顿。

然而,它有两个 stop-the-world 阶段,但是 CMS处理大内存也有点吃力。

启用 G1 垃圾回收器

在 Elasticsearch 的 jvm.options文件中,将默认的垃圾回收器设置为 G1。找到以下行(如果不存在可以添加):

-XX:+UseG1GC

这会告诉 JVM 使用 G1 垃圾回收器来管理内存。

设置 G1堆内存 参数

根据堆内存大小来调整 G1 的相关参数。

第一:确定 G1 垃圾回收器的堆内存区域(Region)大小:

例如,-XX:G1HeapRegionSize参数用于确定 G1 垃圾回收器的堆内存区域(Region)大小。

其默认值会根据堆内存大小自动调整,但在某些情况下可能需要手动优化。

计算公式为:1M <= G1HeapRegionSize <= 32M,并且堆内存大小 / G1HeapRegionSize = Region数量应该是2^n

例如,对于 30GB 的堆内存,可以尝试将-XX:G1HeapRegionSize设置为16M,这样Region数量为30 * 1024 / 16 = 1920(近似值),是一个比较合适的数值。

第二:优化停顿时间相关参数

设置最大停顿时间目标,使用-XX:MaxGCPauseMillis参数来控制 G1 垃圾回收器的最大停顿时间目标。

例如,设置为200表示希望垃圾回收的停顿时间尽量不超过 200 毫秒。

但要注意,设置的值过小可能会导致 G1 频繁地进行垃圾回收以满足停顿时间要求,反而增加了总的垃圾回收时间。

需要根据实际的业务场景和性能测试来调整这个参数。

第三:调整混合垃圾回收(Mixed GC)的触发条件

G1 会在一定条件下触发混合垃圾回收,即同时回收年轻代和部分老年代。

可以通过-XX:InitiatingHeapOccupancyPercent参数来调整触发混合垃圾回收的堆内存占用比例。

例如,将其设置为45,表示当堆内存占用达到 45% 时,G1 开始考虑触发混合垃圾回收。

这个参数的调整需要综合考虑堆内存大小、对象分配速率和停顿时间要求等因素。

3.2.2:jdk17 + ZGC垃圾回收器

45岁老架构师尼恩建议,如果条件允许,可以升级jdk版本,提升性能。

那为什么要升级到jdk17呢,主要是由于:

  • 1、jdk17的g1垃圾回收期相较于jdk11,提升到15%左右。
  • 2、在jdk17中,有向量化支持,同时在字符串处理等方面做了一些优化,相对于集群的写入压力会小很多。
  • 3、在jdk17中,对于ZGC的适配做了一些加强,这个ZGC的垃圾回收器比G1稳定性和性能有很大的提升。

在考虑把jdk的版本升级到jdk17去,同时尽量把elasticsearch的版本更新到最新的版本去。

在完成了jdk17的升级之后,下一步考虑的就是配置ZGC垃圾回收器。

ZGC垃圾回收器是一个并发的、单代的、基于区域的、NUMA 感知的垃圾回收器,Stop-the-world 阶段仅限于根扫描,因此 GC 暂停时间不会随堆或 live set 的变大而增加。

而且ZGC垃圾回收期在回收的过程中几乎是全程并发处理,如下图:

image.png

所以对于短暂停顿的时间来说非常小,几乎可以忽略不计。下面我们来演示下配置ZGC的垃圾回收器方法。

关于zgc的文章,尼恩后续写一个系统化的介绍, 帮助大家穿透zgc。

在elasticsearch的config目录下有一个jvm.options文件

image.png

打开后,找到如下部分:

image.png

因为我们的jdk版本是jdk17,所以找到14-的地方把这里所有的东西注释掉,然后添加如下的代码:

14-:-XX:+UseZGC
14-:-XX:+UnlockExperimentalVMOptions

最后的样子如下图:

image.png

此时我们就配制好了当前的elasticsearch的环境使用ZGC垃圾收集器。

然后我们启动下elasticsearch,启动完成后在日志中查找下ZGC关键词,如下图:

image.png

可以看到启动的时候使用的是ZGC垃圾收集器。

以上就是ZGC垃圾收集器的配置全过程。

1、zgc垃圾收集器从jdk11版本开始支持,所以只要是jdk11及以上的版本都支持zgc垃圾收集器。
2、zgc垃圾收集器的由于是并行进行处理,所以生产环境中ZGC的垃圾收集停顿时间不超过10ms,可以放心大胆的使用起来。
3、使用ZGC是并行的进行垃圾回收,所以有时候会发生查询耗时增加的情况,可以在jvm.options文件中添加如下的参数:-XX:+UseDynamicNumberOfGCThreads 代表动态配置GC线程,降低CPU的使用量,这个是JDK17的新特性,在jdk17以下是没有这个参数的。

具体如下图:

image.png

Elasticsearch的内存优化总结

Elasticsearch的内存配置是其性能优化中非常重要的一部分。

以下是一些关键点:

  • Elasticsearch默认将JVM堆内存设置为1GB或系统物理内存的一半,取较小值。但是这个默认值可能不适合所有场景,因此需要根据实际情况进行调整。
  • 官方建议堆内存大小不要超过32GB,因为当堆大小超过32GB时,JVM会使用64位指针,这会导致更多的内存消耗和额外的开销。如果需要更大的堆内存,可以考虑增加节点数量而不是单个节点的堆内存大小。
  • 堆内存应该合理分配给Elasticsearch进程,同时要确保操作系统和其他进程有足够的内存可用。通常,将机器总内存的50%到60%分配给Elasticsearch是合理的,剩下的内存留给操作系统缓存和其他服务。
  • 对垃圾回收器进行优化,可以使用zgc

4 ElasticSearch集群层面的优化

Elasticsearch的核心架构,是围绕着节点(Node)和集群(Cluster)展开。

节点是Elasticsearch的运行实例,每个节点都承担着特定的任务和职责。

多个节点组成一个集群,通过分布式的方式存储和处理数据,实现了高可用性、可扩展性和高性能。

4.1. Elasticsearch节点概述

image.png

4.1.1 节点的基本概念

Elasticsearch中,节点(Node) 是一个运行中的Elasticsearch实例。

它是集群的基本组成单元,负责存储数据处理查询请求以及与其他节点进行通信

每个节点都有一个唯一的标识符,用于在集群中进行识别和通信。

image.png

4.1.2 节点的类型划分

Elasticsearch中的节点可以根据其功能和角色进行不同的划分,常见的节点类型包括:

4.1.2.1 主节点(Master Node)

主节点 负责管理整个集群的状态,包括节点的加入和离开、索引的创建和删除、分片的分配和迁移等。

主节点并不负责处理数据的存储和查询,它的主要职责是维护集群的元数据和协调各个节点的工作。

以下是一个简单的示例,展示如何在Elasticsearch的配置文件中设置节点为主节点:

node.name: master-node
node.master: true
node.data: false

在上述配置中,node.master 设置为 true 表示该节点可以作为候选主节点,node.data 设置为 false 表示该节点不存储数据。

这里设置的是候选主节点,真正的主节点通过选举产生,45岁老架构师尼恩提示, es 集群只能有一个 真正主节点。

4.1.2.2 数据节点(Data Node)

数据节点 负责存储和检索数据。它是集群中最核心的节点类型,负责处理文档的索引和查询操作。数据节点会将数据按照一定的规则进行分片存储,以实现数据的分布式存储和并行处理。

以下是一个设置数据节点的示例配置:

node.name: data-node
node.master: false
node.data: true

在这个配置中,node.master 设置为 false 表示该节点不作为主节点,node.data 设置为 true 表示该节点是数据节点。

4.1.2.3 协调节点(Coordinating Node)

协调节点 负责接收客户端的查询请求,并将请求转发到合适的数据节点进行处理。协调节点会收集各个数据节点的查询结果,并进行合并和排序,最终将结果返回给客户端。协调节点本身不存储数据,它的主要作用是协调和管理查询请求的处理过程。

以下是一个设置协调节点的示例配置:

node.name: coordinating-node
node.master: false
node.data: false
node.ingest: false
search.remote.connect: false

在这个配置中,除了将 node.masternode.data 设置为 false 外,还将 node.ingestsearch.remote.connect 设置为 false,以确保该节点只作为协调节点。

4.1.3 节点的通信机制

Elasticsearch中的节点通过 TCP 协议进行通信。

节点之间会建立连接,通过发送和接收消息来进行数据的传输和交互。

节点之间的通信是基于请求-响应模型的,一个节点发送请求,另一个节点接收请求并返回响应。

为了提高通信效率和可靠性,Elasticsearch采用了一些优化措施,例如:

  • 连接池:节点会维护一个连接池,用于管理与其他节点的连接。连接池可以复用已建立的连接,减少连接的建立和销毁开销。
  • 消息压缩:在节点之间传输消息时,Elasticsearch会对消息进行压缩,以减少网络传输的数据量。
  • 异步通信:节点之间的通信采用异步方式,发送请求后可以继续执行其他操作,不需要等待响应返回。

4.2 合理划分节点角色

4.2.1 主节点(Master):

主节点负责集群的管理和元数据存储,如索引的创建、删除、分片分配等操作。

为了确保集群的稳定性,应该选择性能稳定、网络连接良好的节点作为主节点。

一般建议将主节点与数据节点分离,避免主节点因处理大量的数据存储和查询任务而负载过重。

例如,在一个大规模的日志分析集群中,可以选择 3 - 5 个配置较高的节点作为master candidate 主节点候选节点,并且这些节点最好分布在不同的机架或数据中心,以防止单点故障。

同时,合理设置最小主节点,通过minimum\_master\_nodes 完成。

minimum\_master\_nodes 设置及其重要,为了防止集群脑裂,这个参数应该设置为法定个数就是 ( master 候选节点个数 / 2) + 1。

minimum\_master\_nodes的定义与作用

minimum_master_nodes是 Elasticsearch 集群中的一个重要设置,用于定义在集群中可以正常选举主节点(Master)的最少候选主节点数量。

minimum\_master\_nodes 的主要目的是防止集群出现 “脑裂(split - brain)” 现象。

脑裂是指在集群中,由于网络分区或其他原因,导致多个节点分别认为自己是主节点,从而使集群分裂成多个独立的部分,这会导致数据不一致和集群的混乱。

通过设置minimum_master_nodes,可以确保在选举主节点时,只有当足够数量的候选主节点能够相互通信时,才能成功选举出主节点,从而避免脑裂情况的发生。

计算minimum\_master\_nodes的合适值

计算minimum_master_nodes的公式为:(master_eligible_nodes / 2) + 1

其中master_eligible_nodes是集群中具有主节点选举资格的节点数量。

例如,在一个有 5 个具有主节点选举资格的节点的集群中,minimum_master_nodes的值应该是(5 / 2) + 1 = 3(向上取整)。

这意味着在进行主节点选举时,至少需要 3 个候选主节点能够相互通信,才能成功选举出主节点。

4.2.2 数据节点(Data)的配置:

数据节点主要负责存储和索引数据,它们会占用大量的磁盘空间和 I/O 资源。根据数据量和查询负载来合理分配数据节点的数量。

如果数据量增长迅速或者查询并发量较高,可以适当增加数据节点。

同时,要注意数据节点的硬件配置,如配备足够的内存来缓存索引数据,使用高性能的磁盘(如 SSD)来提高数据读写速度。

除此之前,数据节点(Data)的配置的配置,和数据规模有关。

预估存储一个亿文档的 Elasticsearch 数据节点数量需要综合多方面因素考虑,以下是一些分析:

第一,评估数据量与存储

单个文档大小估算:假设每个文档平均大小为 1KB,那么一亿个文档大约需要 100GB 的存储空间。如果考虑副本以及索引的额外开销,实际所需空间会更大。

节点存储容量规划:通常情况下,一个数据节点的磁盘容量建议不超过其总容量的 70% 至 80%,以预留一定空间用于数据的临时写入和系统文件存储等。如果使用普通机械硬盘,每个节点的存储容量可以控制在 2TB 至 3TB 左右;如果是 SSD 硬盘,可根据其性能和成本适当增加存储容量,但也不建议超过单节点 5TB 左右的存储量。

第二,规划分片与副本

分片数量:根据经验,分片大小保持在 10GB 至 50GB 之间较为合适。

对于一亿个文档( 100GB),若按每个分片 30GB 左右估算,大约需要 4 至 5 个主分片。

同时,还需要考虑副本数量,通常副本数量可以设置为 1 至 2 个,以提高数据的可用性和查询性能。

节点承载分片数:每个数据节点能够承载的分片数量也有限制,官方一般建议每 GB 堆内存对应少于 20 个分片。

其实,官方的这个建议,这个其实性能很低的。为什么呢 ?

  • 按照官方建议,一个具有 32GB 堆内存的数据节点,最多可承载 640 个分片左右, 对应到的磁盘空间 640 *30G =19200G 。
  • 假设缓存16G,对19200G 的数据进行缓存,当然 这么少的内存,这么大规模的数据,这个内存的缓存的作用,可以说聊胜于无,没啥命中率的。

如果估算数据节点数

怎么估算较好呢?

尼恩的经验, 可以按照 内存需求,反向计算节点数。

一般来说, 最好有磁盘空间的30%以上的内存空间,作为 堆内存。

当然,这个比例越高,性能越好。

比如有磁盘空间的50%以上的内存空间, es的性能越高。

那么, 一亿个文档( 100GB),加速副本最少 200G, 最好有60个G的内存。

假设一个节点 16G内存, 那么 需要 5个节点。

当然,如果有10个节点, 性能会更好。

4.2.3 协调节点(Coordinating)的配置:

协调节点负责接收用户查询请求,并将请求分发到合适的数据节点进行处理,然后汇总结果返回给用户。

协调节点需要有良好的网络和 CPU 性能,以快速地转发和处理查询。

在集群中,可以根据查询流量来设置协调节点的数量,一般可以按照一定的比例(如每 10 个数据节点配置 2 - 3 个协调节点)来配置。

4.3 ES 数据节点的写入调优

ES 数据写入具有一定的延时性,这是为了减少频繁的索引文件产生。另外,在搜索引擎的业务场景下,用户一般并不需要那么高的写入实时性。比如 在网站发布一条征婚信息,或者二手交易平台发布一个商品信息。其他人并不是马上能搜索到的,这其实也是正常的处理逻辑。

默认情况下 ES 每秒生成一个 segment 文件,当达到一定阈值的时候 会执行merge,merge 过程发生在 JVM中,频繁的生成 Segmen 文件可能会导致频繁的触发 FGC,导致 OOM。

为了避免避免这种情况,通常采取的手段是降低 segment 文件的生成频率,手段有两个

  • 一个是 增加时间阈值,
  • 另一个是增大 Buffer的空间阈值,因为缓冲区写满也会生成 Segment 文件。

生产经常面临的写入可以分为两种情况:

  • 高频低量:高频的创建或更新索引或文档一般发生在 处理 C 端业务的场景下。
  • 低频高量:一般情况为定期重建索引或批量更新文档数据。

4.3.1 写入优化的目标

写入调优均,常常以提升写入吞吐量和并发能力为目标,而非提升写入实时性。

4.3.2.增加 flush 时间间隔,

目的是减小数据写入磁盘的频率,减小磁盘IO频率。

在 Elasticsearch 中,flush操作主要是将索引缓冲区(Index Buffer)中的数据刷新到磁盘上的索引文件中。

当新的数据写入 Elasticsearch 时,会先存储在内存的索引缓冲区中。flush操作会将这些缓冲区中的数据持久化,以确保数据的安全性和可恢复性。

同时,flush操作还会更新索引的相关元数据,如提交点(Commit Point)等。

通过配置文件修改flush时间间隔

在 Elasticsearch 的elasticsearch.yml配置文件中,可以找到与flush相关的参数。

主要参数是index.translog.flush_threshold_period,它用于控制flush的时间间隔。

这个参数的默认值是30s,表示每隔 30 秒会自动进行一次flush操作。

修改时间间隔要增加flush时间间隔,例如将其设置为60s,可以在elasticsearch.yml配置文件中添加或修改以下内容:

index.translog.flush_threshold_period: 60s

修改完成后,保存配置文件并重启 Elasticsearch 服务,新的flush时间间隔就会生效。

尼恩提示,增加flush时间间隔意味着数据在内存缓冲区中停留的时间更长。

如果在这段时间内发生服务器故障或 Elasticsearch 进程意外终止,可能会导致缓冲区中的数据丢失。

因此,在增加flush时间间隔的同时,需要权衡数据安全性和性能之间的关系。

如果数据的可靠性要求极高,不建议过长地延长flush时间间隔。

4.3.3 增加refresh_interval的参数值

在 Elasticsearch 中,refresh_interval是一个用于控制索引数据可见性的重要参数。它定义了索引数据被刷新(Refresh)的时间间隔。当新的数据写入索引后,并不是立即可以被搜索到,而是要等待索引被刷新。刷新操作会使新写入的数据对搜索可见。

具体来说,Elasticsearch 会在内存中维护一个数据结构来存储索引数据,这个数据结构在刷新之前是不断更新的。

refresh 刷新操作, 会将这个内存中的数据结构更新到磁盘上的索引文件(倒排索引等)中,并且更新相关的搜索视图,使得新数据能够被查询到。

增加refresh_interval的参数值 目的,是减少segment文件的创建,减少segment的merge次数,merge是发生在jvm中的,有可能导致full GC,增加refresh会降低搜索的实时性。

ES的 refresh 行为非常昂贵,并且在正在进行的索引活动时经常调用,会降低索引速度。

refresh\_interval的默认值和取值范围

默认值:默认的refresh_interval1s。这意味着每隔 1 秒,索引会被刷新一次,新写入的数据会在 1 秒后对搜索可见。

取值范围:可以设置为一个时间值,如-1(表示禁用自动刷新,需要手动刷新)、30s1m等。时间单位可以是秒(s)、分钟(m)、小时(h)等。

默认情况下,Elasticsearch 每秒(1s ) 定期刷新索引,但仅在最近 30 秒内收到一个或多个搜索请求的索引上。

如果没有搜索流量或搜索流量很少(例如每 5 分钟不到一个搜索请求), 并且想要优化索引速度,这是最佳配置。

此行为旨在在不执行搜索的默认情况下自动优化批量索引。建议显式配置此配置项,如 30秒。

如何修改refresh\_interval? 通过配置文件修改,在elasticsearch.yml配置文件中,可以通过添加或修改以下内容来设置refresh\_interval

index.refresh_interval: 30s

这会将所有新创建的索引的刷新间隔设置为 30 秒。

如果只想对特定的现有索引进行设置,可以使用索引设置 API。发送一个 PUT 请求到

http://<es - node - ip>:9200/<index - name>/_settings

,其中是要修改的索引名称。请求体如下:

{
   
  "index": {
   
    "refresh_interval": "30s"
  }
}

这种方式可以在不重启 Elasticsearch 的情况下,动态地修改特定索引的刷新间隔。

延长refresh_interval可以提高索引的写入性能。因为刷新操作是一个相对较重的操作,它涉及到内存数据结构到磁盘索引的更新以及搜索视图的更新。减少刷新频率可以减少磁盘 I/O 和 CPU 的消耗,使得更多的资源可以用于数据写入。例如,在大数据量的批量写入场景下,将refresh_interval延长到 30 秒或更长,可以显著提高写入速度。

但是,延长refresh_interval会导致新数据对搜索可见的延迟增加。如果应用场景对数据的实时性要求很高,如实时搜索系统,可能需要谨慎考虑延长刷新间隔的影响。

4.3.4 写入的时候, 增加Buffer大小

本质也是减小refresh的时间间隔,因为导致segment文件创建的原因不仅有时间阈值,还有buffer空间大小,写满了也会创建。

默认最小值 48MB< 默认值 JVM 空间的10% < 默认最大无限制

4.3.5 写入的时候, 关闭副本

当需要单次写入大量数据的时候,建议关闭副本,暂停搜索服务,或选择在检索请求量谷值区间时间段来完成。

  • 第一是减小读写之间的资源抢占,读写分离
  • 第二,当检索请求数量很少的时候,可以减少甚至完全删除副本分片,关闭segment的自动创建以达到高效利用内存的目的,因为副本的存在会导致主从之间频繁的进行数据同步,大大增加服务器的资源占用。

具体可通过则设置index.number_of_replicas0以加快索引速度。

没有副本意味着丢失单个节点可能会导致数据丢失,因此数据保存在其他地方很重要,以便在出现问题时可以重试初始加载。

初始加载完成后,可以设置index.number_of_replicas改回其原始值。

4.4 数据分片设置

4.4.1 数据分片的预估

ES一旦创建好索引后,就无法调整分片的设置,而在ES中,一个分片实际上对应一个lucene 索引,而lucene索引的读写会占用很多的系统资源,因此,分片数不能设置过大;

所以,在创建索引时,合理配置分片数是非常重要的。

一般来说,我们遵循一些原则:

  1. 控制每个分片占用的硬盘容量,不超过ES的最大JVM的堆空间设置(一般设置不超过32G,参加上文的JVM设置原则),
    因此,如果索引的总容量在500G左右,那分片大小在16个左右即可。
  2. 一般都设置分片数不超过节点数的3倍。
    考虑一下node数量,一般一个节点有时候就是一台物理机,如果分片数过多,大大超过了节点数,很可能会导致一个节点上存在多个分片,
    一旦该节点故障,即使保持了1个以上的副本,同样有可能会导致数据丢失,集群无法恢复。
    所以, 一般都设置分片数不超过节点数的3倍。

4.4.2 分片分配策略

ES使用数据分片(shard)来提高服务的可用性,将数据分散保存在不同的节点上以降低当单个节点发生故障时对数据完整性的影响,同时使用副本(repiica)来保证数据的完整性。

关于分片的默认分配策略,在7.x之前,默认5个primary shard,每个primary shard默认分配一个replica,即5主1副,而7.x之后,默认1主1副

ES的 分片分配策略,大致如下:

  • ES在分配单个索引的分片时会将每个分片尽可能分配到更多的节点上。但是,实际情况取决于集群拥有的分片和索引的数量以及它们的大小,不一定总是能均匀地分布。
  • Paimary只能在索引创建时配置数量,而replica可以在任何时间分配,并且primary支持读和写操作,而replica只支持客户端的读取操作,数据由es自动管理,从primary同步。
  • ES不允许Primary和它的Replica放在同一个节点中,并且同一个节点不接受完全相同的两个Replica
  • 同一个节点允许多个索引的分片同时存在。

4.4.3 分片的数量

避免分片过多

大多数搜索会命中多个分片。

每个分片在单个 CPU 线程上运行搜索。

虽然分片可以运行多个并发搜索,但跨大量分片的搜索会耗尽节点的搜索线程池。这会导致低吞吐量和缓慢的搜索速度。

分片越少越好

每个分片都使用内存和 CPU 资源。在大多数情况下,一小组大分片比许多小分片使用更少的资源。

4.4.4 分片的大小决策

分片的合理容量:10GB-50GB。

虽然不是硬性限制,但 10GB 到 50GB 之间的分片往往效果很好。

根据网络和用例,也许可以使用更大的分片。在索引的生命周期管理中,一般设置50GB为单个索引的最大阈值。

堆内存容量和分片数量的关联

小于20分片/每GB堆内存,一个节点可以容纳的分片数量与节点的堆内存成正比。

例如,一个拥有 30GB 堆内存的节点最多应该有 600 个分片。

如果节点超过每 GB 20 个分片,考虑添加另一个节点。

正文到此结束
最后修改:2025 年 05 月 22 日
如果觉得我的文章对你有用,请随意赞赏