背景
ES7 相比于 ES6 有多个层面的优化,对于开源的ES而言,升级是必经之路。
ES的使用场景非常多,在升级过程中可能会遇到非预期的结果;
比如之前文章提到的典型案例:ES7.17版本terms查询性能问题
ES7.17版本terms查询性能问题_es terms查询慢 profile-CSDN博客
接下来再分析一个升级之后聚合性能下降的case;
场景
1. 按照标准最佳实践进行改造(分片控制在20G左右,term/terms查询使用keyword类型...)
2. 集群从ES6.8.5 升级到 ES7.17.5
3. 异常:升级后性能骤降
avg 100ms -> 400ms
特定聚合查询 400ms -> 1500ms
4. 通过 profile ,以及实际测试,耗时主要是在聚合
分析
1. 性能下降,随之而来的还有CPU升高
2. 通过 arthas 工具采集火焰图
可以看到和global_ordinals全局序数相关;(查询/聚合时临时构建全局序数)
3. 针对全局序数,一般是2种优化手段
方案一:不使用序数 "execution_hint": "map"
注:只有在聚合时基数较小的情况下适用,否则容易OOM;(即字段整体数据基数较小;或者聚合时需要带有查询条件,查询条件筛选后用于聚合的字段数据基数比较小)
"aggregations": {
"agg_count": {
"terms": {
"field": "xxxxID",
"execution_hint": "map",
"size": 2,
"min_doc_count": 1,
"shard_min_doc_count": 0,
"show_term_doc_count_error": false,
"order": [
{
"_key": "asc"
}
]
}
}
}
方案二:在查询之前提前创建好全局序数 "eager_global_ordinals": true,将全局序数的构建转移到索引(写数据)阶段,保证查询的效率
PUT xxxx/_mapping
{
"properties": {
"xxxxxID":{
"type": "keyword",
"eager_global_ordinals": true
}
}
}
结论
1. 使用方案一,"execution_hint": "map",性能大幅提升
1600ms -> 10ms (没有pageCache时候是40ms左右)
注:POST xxxx/_cache/clear 只能清理ES本身的缓存,无法清理pageCache
然后check一下terms聚合使用的字段的基数
POST xxxxx/_search
{
"query": {
"match_all": {}
},
"size": 0,
"aggs": {
"count_aggs": {
"cardinality": {
"field": "xxxxID"
}
}
}
}
整个索引,该字段的基数为 8kw,如果聚合不带任何条件就会消耗大量内存;
如果聚合都带查询条件,那么该方案适用,并且性能提升 90%+;
2. 方案二需要重建索引,理论查询性能也会大幅提升,此处暂无测试数据
PUT xxxx/_mapping
{
"properties": {
"xxxxxID":{
"type": "keyword",
"eager_global_ordinals": true
}
}
}