Elasticsearch 性能调优,分层说明。
集群层
集群规划
部署集群时,应该根据具体业务来评估初始集群大小,这些信息包括但不限于:
数据总量,每天的增量;
查询类型和搜索并发,QPS。
另一方面,需要控制最大集群规模和数据总量:
节点总数不应该太多:一般来说,最大集群规模最好控制在 100 个节点左右。经测试,在上千个节点的集群规模下,节点间的连接数和通信量倍增,主节点管理压力比较大。
单个分片不要超过 50 GB:最大集群分片总数控制在几十万的级别。太多分片同样增加了主节点的管理负担,而且集群重启恢复时间会很长。
建议为集群配置较好的硬件,搜索和分析对 CPU、内存、磁盘的性能要求都很高。
独立部署主节点
将主节点和数据节点分离部署的好处是主备切换可以迅速完成。因为独立部署的主节点没有存储数据,旧的主节点离线不会产生未分配状态的分片,避免分片重新分配,新的主节点被选出后集群状态可以迅速变为 Green。
节点层
适当的节点角色
为不同的节点赋予适当的角色,比如主节点和数据节点分类,部署单独的节点作为协调节点等。
节点大小
Elasticsearch 不建议为 JVM 配置超过 32GB 的内存,当 JVM 堆内存超过 32GB 时,Java 内存指针压缩失效,浪费内存,降低 CPU 性能,GC 压力也较大。因此推荐设置为 31GB,并确保堆内存最小值(Xms)与最大值(Xmx)大小相同,防止程序在运行时动态改变堆内存大小,这是个比较消耗系统资源的过程。
内存分配
搜索操作很依赖对系统 Cache 的命中,建议把 50% 的可用内存作为 Elasticsearch 的堆内存,为 Lucene 保留剩下的 50% 可用内存,用作系统 Cache。
控制线程池队列大小
不要为 bulk 和 search 分配过大的队列,队列并非越大越好,队列缓存的数据越多,GC 压力越大,默认的队列大小基本够用了,即使在压力测试的场景中,默认队列大小也足以支持。
线程池线程数
Elasticsearch 默认的线程设置已经是很合理的了。对于所有的线程池,线程个数是根据 CPU 核心数设置的。处理器的计算能力是有限的,一个处理器同时只能运行一个线程,拥有更多的线程会导致你的处理器频繁切换线程上下文。当处理器需要切换到其它不同的线程的时候,它会存储当前的状态(寄存器等等),然后加载另外一个线程。如果幸运的话,这个切换发生在同一个核心,如果不幸的话,这个切换可能发生在不同的核心,这就需要在内核间总线上进行传输。
所以请不要调整线程池的线程数。如果你真想调整,一定要关注你的 CPU 核心数,最多设置成核心数的两倍,再多了可能就是浪费。
系统层
禁止交换分区
操作系统为了缓解内存不足对应用程序的影响,允许把一部分内存中的数据换到磁盘上,以达到应用程序对内存使用的缓冲,这些内存数据被换到磁盘上的区域,就是 Swap。
一般在安装操作系统的时候直接关闭交换分区。也可以在配置文件中对内存进行锁定,以避免交换内存。
bootstrap.mlockall: true
索引层
适当的副本数
大部分场景下,将副本数 number_of_replicas 设置为 1 即可,每个分片都存在两份数据。如果对搜索请求的吞吐量要求较高,可以适当增加副本数量,让搜索操作可以利用更多的节点。如果在项目初始阶段不知道多少副本数够用,则可以先设置为 1,后期再动态调整。对副本数的调整只会涉及数据复制和网络传输,不会重建索引,因此代价较小。
Force Merge
在系统的空闲时间段对不再更新的只读索引执行 Force Merge,它带来的好处如下:
可以大幅减少进程需要打开的文件描述符;
可以加快搜索过程,因为搜索需要检索全部分段;
单个分段加载到内存时也比多个分段更节省内存占用;
可以加快索引恢复速度。
Close 索引
关闭的索引除存储空间外不占用其他资源,对于暂时不再读写的所以可以将这些索引关闭,需要的时候再打开。
curl -X POST "localhost:9200/my_index/_close"curl -X POST "localhost:9200/my_index/_open"
读写
Elasticsearch 的默认设置是综合考虑数据可靠性、搜索实时性、写入速度等因素的。当追求极致的读写速度时,往往是以牺牲可靠性和搜索实时性为代价的。
调整 translog flush 间隔
从 Elasticsearch 2.x 开始,默认设置的 translog 的持久化策略为:每个请求都执行 flush 操作,这是影响 Elasticsearch 写入速度的最大因素。但是只有这样,写操作才有可能是可靠的,在之前版本中默认 5S 执行一次 flush 操作,这意味着可能出现 5S 的数据丢失。如果系统可以接受一定概率的数据丢失,则调整 translog 持久化策略为周期性和一定大小的时候执行 flush 操作。
调整 index refresh 间隔
默认情况下索引的 refresh_interval 属性为 1S,这意味着数据写 1 秒后就可以被搜索到,每次索引的 refresh 会产生一个新的 Lucene 段,这会导致频繁的 segment merge 行为。如果不需要很高的搜索实时性,应该降低索引 refresh 频率。
限制结果集大小
我们在搜索流程中,为了降低协调节点的合并压力,我们需要限制结果集大小,如果确实需要很大的结果集,则应该使用 Scroll API。
避免索引巨大的文档
大型文档会给网络、内存和磁盘造成了更大的压力。http.max_context_length 属性的默认值为 100MB,Elasticsearch 会拒绝索引超过此大小的文档。
索引一个文档所需内存大小是原始文档大小的几倍,尤其是邻近(Proximity)搜索和高亮会变得更加昂贵,它们的成本直接取决于原始文档大小。
避免请求同一个协调节点
无论索引文档还是执行搜索请求,客户端都应该避免将请求发送到固定的某个或少数几个节点,因为少数几个协调节点作为整个集群对外的读写节点的情况下,它们很有可能承受不了那么多的客户端请求。尤其是搜索请求,协调节点的合并及排序会占用比较高的内存和 CPU,聚合会占用更多内存。因此会导致给客户端的返回慢,甚至导致节点 OOM。
尽可能使用自动生成的 ID
Elasticsearch 自动生成的 ID 保证是唯一的,能避免版本查找。如果客户真的需要使用自定义的 ID,我们建议选择一个对 Lucene 友好的 ID,比如零填充顺序 ID、UUID-1 或者纳秒级时间。这些 ID 具有一致的顺序模式,能良好压缩。相比之下,像 UUID-4 这样的 ID 本质上是随机的,压缩率低,会降低 Lucene 的速度。
适当的副本数
和前面说的一样,大部分场景下,将副本数 number_of_replicas 设置为 1 即可,每个分片都存在两份数据。如果对搜索请求的吞吐量要求较高,可以适当增加副本数量,让搜索操作可以利用更多的节点。
文章同步 github。
地址:github.com/lazecoding/Note/blob/main/note/articles/es/调优.md




