🌹 以上分享从入门到进阶 之 ElasticSearch 配置优化篇,如有问题请指教写。
🌹🌹 如你对技术也感兴趣,欢迎交流。
🌹🌹🌹 如有需要,请👍点赞💖收藏🐱🏍分享
集群参数
硬件优化
Elasticsearch 重度使用磁盘,你的磁盘能处理的吞吐量越大,你的节点就越稳定。
优化磁盘 IO 的技巧
分片策略
原则
推迟分片分配
对于节点瞬时中断的问题
默认情况,集群会等待一分钟来查看节点是否会重新加入,如果这个节点在此期间重新加入,重新加入的节点会保持其现有的分片数据,不会触发新的分片分配。这样就可以减少 ES 在自动再平衡可用分片时所带来的极大开销。
修改参数 delayed timeout ,可以延长再均衡的时间,可全局设置也可以在索引级别进行修改:
PUT /_all/_settings
{
"setting": {
"index.unassigned.node_left.delayed_timeout":"5m"
}
}
路由选择
当我们查询文档的时候,Elasticsearch 如何知道一个文档应该存放到哪个分片中呢?
通过下面这个公式来计算:
shard = hash(routing) % number_of_primary_shards
routing 默认值是文档的 id,也可以采用自定义值,比如用户 id。
不带 routing 查询
在查询的时候不知道要查询的数据具体在哪个分片上,整个过程分 2 个步骤
分发:
请求到达协调节点后,协调节点将查询请求分发到每个分片上。
聚合:
协调节点搜集到每个分片上查询结果,在将查询的结果进行排序,返回用户结果。
带 routing 查询
根据 routing 信息定位到某个分配查询,不需要查询所有的分配,经过协调节点排序。
写入速度优化
ES 的默认配置,是综合了数据可靠性、写入速度、搜索实时性等因素,根据实际需求进行偏向化配置。
写优化,搜索要求不高
- 加大 Translog Flush ,目的是降低 Iops、Writeblock.
- 增加 Index Refresh 间隔,目的是减少 Segment Merge 的次数。
- 调整 Bulk 线程池和队列。
- 优化节点间的任务分布。
- 优化 Lucene 层的索引建立,目的是降低 CPU 及 IO
批量数据提交
ES 提供了 Bulk API 支持批量操作,有大量的写任务时,可以使用 Bulk 来进行批量写入。
通用的策略如下:
优化存储设备 (SSD)
合理使用合并
Lucene 以段的形式存储数据。当有新的数据写入索引时,Lucene 就会自动创建一个新的段。
随着数据量的变化,段的数量会越来越多,消耗的多文件句柄数及 CPU 就越多,查询效率下降。
Lucene 段合并的计算量庞大,会消耗大量的 I/O,ES 默认采用较保守的策略,让后台定期进行段合并
减少 Refresh 的次数
Lucene 在新增数据时,采用了延迟写入的策略,默认 索引的 refresh_interval 为 1 秒。
Lucene 将待写入的数据先写到内存中,超过 1 秒(默认) 时就会触发一次 Reftesh, Refiesh 会把内存中的的数据刷新到操作系统的文件缓存系统。
对搜索的实效性要求不高,可以将 Refiesh 周期延长,可以有效地减少段刷新次数,但需要消耗更多的 Heap 内存
加大Flush 设置
Flush 目的是把文件缓存系统中的段持久化到硬盘,当 Translog 的数据量达到
512MB 或者 30 分钟时,会触发一次 Flush。
imndex.translog.flush_threshold_size 参数的默认值是 512MB,
增加参数值意味着文件缓存系统中可能需要存储更多的数据,需要为操作系统的文件缓存系统留下足够的空间。
减少副本数量
ES 为了保证集群的可用性,提供了 Replicas(副本)支持,每个副本也会执行分析、索引及可能的合并过程,Replicas 的数量严重影响写索引的效率。
当写索引时,需要把写入的数据都同步到副本节点,副本节点越多,写索引的效率就越慢。
如果我们需要大批量进行写入操作,可以先禁止 Replica 复制,设置index.number_of_replicas:0 关闭副本。在写入完成后,Replica 修改回正常的状态。
内存设置
原则
不要超过物理内存的 50%
Lucene 的设计目的是把底层 OS 里的数据缓存到内存中。Lucene 的段存储到单个文件中的,这些文件都是不会变化的,利于缓存,操作系统也会把这些段文件缓存起来,以便更快的访问。如果我们设置的堆内存过大,Lucene 可用的内存将会减少,就会严重影响降低 Lucene 的全文本查询性能。
堆内存的大小最好不要超过 32GB
在 Java 中,所有对象都分配在堆上,然后有一个 Klass Pointer 指针指向它的类元数据。
假设你有个机器有 128 GB 的内存,你可以创建两个节点,每个节点内存分配不超过 32 GB。
不超过 64 GB 内存给 ES 的堆内存,剩下的超过 64 GB 的内存给 Lucene
性能优化
缓存优化
页缓存
为了数据的安全、可靠,常规操作中,数据都是保存在磁盘文件中的。对数据的访问,绝大数情况下是对文件的访问,为了提升对文件的读写的访问效率,Linux 内核会以页大小(4KB)为单位,将文件划分为多个数据块。当用户对文件中的某个数据块进行读写操作时,内核首先会申请一个内存页(称为 PageCache 页缓存) 与文件中的数据块进行绑定。
页缓存的基本理念是从磁盘读取数据后将数据放入可用内存中,下次读取时从内存返回数据,而获取数据不需要进行磁盘查找。对应用程序来说是完全透明的,应用程序发出相同的系统调用,操作系统使用页缓存而不从磁盘读取。
Java 程序是跨平台的,没有和硬件(磁盘,内存)直接交互的能力,想要和磁盘文件交互,须要通过 OS 操作系统来完成文件的读写,就称之为用户态转换为内核态。操作系统对文件进行读写时,实际是对文件的页缓存进行读写。
对文件进行读写操作时,分以下两种情况
当从文件中读取数据时
页缓存存在
直接把页缓存的数据拷贝给用户。
页缓存不存在
内核首先会申请一个空闲的内存页(页缓存),然后从文件中读取数据到页缓存,并且把页缓存的数据拷贝给用户。
当向文件中写入数据时
页缓存存在
那么直接把新数据写入到页缓存即可。
页缓存不存在
内核首先会申请一个空闲的内存页(页缓存),并且把新数据写入到页缓存中。
对于被修改的页缓存,内核会定时把这些页缓存刷新到文件中。
分片级请求缓存
协调节点
对一个或多个索引发送搜索请求时,搜索请求首先会发送到 ES 集群中的某个节点
本地结果集
协调节点会把该搜索请求分发给其他节点并在相应分片上执行搜索操作,把分片上的执行结果称为“本地结果集”,
分片再将执行结果返回给协调节点;协调节点获得所有分片的本地结果集之后,合并成最终的结果并返回给客户端。
Elasticsearch 会在每个分片上缓存了本地结果集,频繁使用的搜索请求立即返回结果,称之为 Request Cache,Shard Request Cache,即分片级请求缓存。
Request Cache 默认时关闭的,可在创建新索引时启用
PUT /索引名 -d
{
"setting": {
"index.requests.cache.enable": true
}
}
PUT 服务器IP:端口/索引名/setting -d
{
"index.requests.cache.enable": true
}
开启缓存后,需要在搜索请求中加上 request cache=true 参数,才能使查询请求被缓存
GET /索引名/_search?request_cache=true&pretty
{
"size": 20,
"aggs": {
"pops_color": {
"terms": {
"name": "华为"
}
}
}
}
查询缓存
Elasticsearch 具有 IndicesQueryCache 类。这个类与 IndicesService 的生命周期绑定,按节点的特性 一 这样做是有道理的,缓存本身使用了 Java 堆。
两个配置选项
indices.queries.cache.count: 缓存条目总数,默认为 10000
indices.queries.cache.size: 用于此缓存的 Java 堆的百分比,默认为 10%
冻结层和可搜索快照
引入了冷层,通过消除在本地存储几余副本,在相同数量的硬件上最多存储两倍于热层的数据。为了获得最佳性能,主数据仍然存储在本地,但冷层中的索引由存储在对象存储中的可搜索快照提供支持,以实现冗余。