日期: August 24, 2023
关于elasticsearch数据写入流向及在对应阶段的优化措施
ES数据写入流程
内存—refresh—>缓存(文档可被查到)—flush—>磁盘(数据不丢失)
What’s Refresh
指数据写入从内存到文件系统缓存的过程,每次refresh都会创建一个新的segment,此时新的segment为可读状态,即文档可被查到,但仍处于缓存中尚未落盘
相关参数
index.refresh_interval: 30s
默认情况下索引的refresh_interval为1秒,这意味着数据写1秒后就可以被搜索到
每次索引refresh会产生一个新的Lucene段,即segment,segment在到一定条件后会自动合并,refresh间隔过短会导致频繁的segment merge行为,如果不需要高的搜索实时性,应该降低索引refresh周期
我们可以手动refresh保证文档可立即被读到
- 全局索引 POST /_refresh
- 指定索引 POST /index/_refresh
What’s Flush
指触发lucene commit,将缓存中的segment写入到磁盘,写入一个包含所有段列表的提交点,并清空translog日志文件
Translog
事务日志,每一次es的变更操作都会写入translog,比如文档进入内存缓冲区时也会追加进translog,translog周期性刷盘,默认5s,或者达到固定大小(默认512MB)后刷盘,可类比MySQL的binlog
相关参数
index.translog.sync_interval
index.translog.durability: request
默认设置下,translog的持久化策略为:每个请求都flush
如果可以接受一定概率的数据丢失(例数据写入主分片但尚未复制到副分片时主机断电,数据既没有刷到Lucene,translog也没有刷盘,恢复时translog中没有这个数据则数据丢失)
可调整translog持久化策略为周期性和一定大小时flush
index.translog.durability: async
index.translog.flush_threshold_size: 512MB
index.translog.sync_interval: 120s
async表示translog的刷盘策略按sync_interval周期进行
translog刷盘间隔时间默认5s,不可低于100ms
Merge &Segment
上述提到
每次索引的refresh会产生一个新的Lucene段,即segment
Or
indexing buffer满时(比如大量的创建索引)也会生成新的段
什么是segment
一个索引包含多个分片——>每个分片都有一个Lucene index——>每个Lucene包含多个Segment(倒排索引)——>每个Segment最终落盘为一个个文件
Segment过多影响
- 消耗资源:每一个段都会消耗文件句柄、内存和cpu
- 搜索变慢:每个搜索请求都必须轮流检查每个段;所以段越多,搜索也就越慢
段合并目的
- 提高搜索速度
- 减少索引容量
es会在后台自动段合并,小段合成中段,中段合成大段
段合并做了什么
delete doc时只是标记删除,物理数据仍然存在,段合并时标记删除的doc不会拷贝到大段中,合并过程中并不会中断查询,但会导致磁盘io消耗和检索性能
相关参数
refresh_inteval影响段的生成数量
index.merge.scheduler.max_thread_count影响段合并的速度
Indexing buffer
indexing buffer在为doc建立索引时使用,当buffer满时会刷入磁盘,生成一个新的segment,这是除refresh_interval刷新索引外,另一个生成新segment的机会
每个shard有自己的indexing buffer
indices.memory.index_buffer_size 默认为堆空间的10%
indices.memory.min_index_buffer_size 默认48MB
indices.memory.max_index_buffer_size 默认无限制
在执行大量的索引操作时,indices.memory.index_buffer_size的默认设置可能不够,这和可用堆内存、单节点上的shard数量相关,可以考虑适当增大该值,增大该值,减少segment,就会减少merge
索引冻结/关闭
上述提到index buffer,每个索引都会占用堆内存,那么对于不需要查询或者查询频率低的索引我们可以通过关闭/冻结的形式释放这部分内存
关闭打开
- post /index_name/_close
- post /index_name/_open
冻结解冻
- post /index_name/_freeze
- post /index_name/_unfreeze
查询冻结的索引
加上参数 &ignore_throttled=false
- GET /index_name/_search?&ignore_throttled=false
为什么需要冻结索引
对于旧数据而言,即使我们把这些索引放在冷节点上,它依然占用堆内存,当然我们可以选择直接close索引,但如果需要查的时候又要重新打开,很麻烦,所以如上所述,对于冻结的索引我们只需要查询时加上ignore_throttled=false将数据结构重新加载到内存中,此时因为没有了缓存,查询冻结索引速度会下降,当然在索引冻结前可以手动段合并为一个大段,降低性能开销
调整字段Mappings
减少字段数量
将不需要建立索引的字段index属性设置为not_analyzed或no
减少字段内容长度
禁用_all字段,可以明显降低对CPU和I/O的压力
写入速度优化
所以综上所述提升写入速度可从以下几方面入手:
- 加大translog flush间隔,目的是降低iops、writeblock
- 加大index refresh间隔,除了降低I/O,重要的是降低segment merge频率
- 调整bulk请求
- 优化磁盘间的任务均匀情况,将shard尽量均匀分布到主机的各个磁盘
- 优化节点间的任务分布,将任务尽量均匀地发到各节点
- 优化Lucene层建立索引的过程,降低CPU占用率及I/O,例如,禁用_all字段