ES集群查询稳定性优化(二)
承继这篇文章,本文从“段文件”数量对 ES 查询性能影响的角度,结合 ES 索引构建 的工程实践,谈谈如何优化 ES 查询稳定性。
一篇严格的稳定性优化文章本应该贴很多“指标优化”数据,比如
- 优化前查询 TP99 是多少,优化后 TP99变成了多少?
- 尖刺查询数量的变化?
- GC 次数的变化?
....
这些数据不太好放出来,因此:仅从理论角度/方法论角度来谈一些优化措施。
ES官方文档 tune-for-search-speed,其中,提到了段文件对查询性能的影响,并指出单个段文件对查询性能是很友好的。
Shards that have been force-merged into a single segment can use simpler and more efficient data structures to perform searches.
多个段文件对查询性能的影响是:
往ES索引写入doc时,会周期性地 refresh 生成 段文件,段文件的合并又会影响缓存的有效性,从而影响查询稳定性:
查询请求分发到 index 的所有 shard 上,Lucene 会对 shard 上的所有段文件进行搜索,过滤掉已删除的doc,并且最终合并各个段文件上的搜索结果。为防止段文件数量过多影响搜索性能,Lucene 有专门的线程负责段文件的合并。(参考:found-elasticsearch-from-the-bottom-up)
因此,将数据拆分成:全量索引和增量索引,并且全量索引占据大部分数据,由于全量索引只有一个段文件,并且几乎不会修改,因此不会有段文件生成/合并,最大限度地避免影响查询请求。
文章中提到的其它查询优化手段按下不表,比如设置合理的 mapping 结构,根据字段的实际使用情况设置 index 属性、doc_value属性、store 属性等、不需要范围查询的数值型字段可考虑设置成 keyword......毕竟这属于ES固有的优化手段,与业务系统的架构设计无关
基于此,我们可以将一个业务的数据划分成:全量T+1数据和增量实时数据。其中,全量T+1数据对应一个 ES 索引,增量实时数据对应一个ES索引。示例如下:
sku_index_20231126
sku_index_20231126_realtime
sku_index_20231126 存储当前全量索引数据,而 sku_index_20231126_realtime 存储当天增量更新的 doc。
然后,针对全量T+1数据,在索引构建时,确保每个分片只有一个段文件,一旦全量索引构建完成,就几乎不再改动,且每个分片/shard 下只有一个 segment 文件,从而提升了 ES 查询性能的稳定性,即:
- 减少了尖刺查询的数量
- 整体查询性能 TP99 也有提升
总结下:
- 在构建全量索引时,先计算好这个索引需要多少个分片,然后在索引构建时,将每个分片merge成只有一个段文件才会将该索引上线,承接线上查询请求,从而提高了线上查询稳定性和性能。
- insert 事件由增量索引承接,不影响全量索引、update事件先删全量索引doc,再写入增量索引 doc,delete 事件从全量索引中删除doc,增量索引的 doc 数据量一般远小于全量索引。
- 将索引拆分成:全量+增量2个索引,从 query 查询角度看,类似于:读写分离,尽量减少写操作对查询的影响。也即:全量索引几乎没有 refresh 操作,而增量索引因为数据变更,会存在 refresh 生成新的 segment 文件。