cardinality(基数)度量
本质是一个基于HyperLogLog++(HLL)算法的一个近似聚合方案,可以通过precision_threshold参数调整聚合精确度,precision_threshold越大越精准,只接受0–40000之间的数字,更大的值会被当作40000来处理。(HLL只需要字段内容的哈希值)。
GET /zmc_index/_search
{
"size" : 0,
"aggs" : {
"distinct_colors" : {
"cardinality" : {
"field" : "color",
"precision_threshold" : 100
}
}
}
}
在一定场景中(例如只有少量写入的情况下),可以通过将需求字段改成一个多值字段,来完成优化(牺牲写入,优化查询、聚合)。
PUT new_index/
{
"mappings": {
"_doc": {
"properties": {
"color": {
"type": "keyword",
"fields": {
"hash": {
"type": "murmur3"
}
}
}
}
}
}
}
类似的优化方案还有ES的分词使用场景,一般会选择多种分词器,例如pinyin、ik、ngram,对同一个字段使用不同的分词器索引出多个值,方便搜索时候查询(查准)。
更具体可以参考官方解释:
https://www.elastic.co/guide/cn/elasticsearch/guide/current/cardinality.html
Global ordinals(全局字典)(该映射是shard级别的,所有segment公用一个字典)
默认的模式就是使用Global ordinals,ES(默认)假设会有海量的数据,那么在聚合的时候就不合适全部放到内存,于是有了这个结构。其实就是一个全局的映射,把keyword或者term(词元)映射成一个字典值,然后保证聚合的时候的速度,同时也节省了内存(字典值会比原始的值小很多)。
global ordinals在shard上被触发refresh以后就会失效,下次使用的时候需要再重新构建。
可以使用eager_global_ordinals,在每次refresh后即可更新字典,字典常驻内存,减少了查询的时候构建字典的耗时。
PUT zmc_index/_mapping
{
"properties": {
"field": {
"type": "keyword",
"eager_global_ordinals": true
}
}
}
用户使用execution_hint:map效果更快?
有用户在调优的时候调整了execution_hint参数,将其改成了map,即不使用global ordinals模式;
map的模式:聚合的时候在内存里面做分组(分桶)(适用于小数据量)
global ordinals比map慢的原因:字典需要在查询的时候构建(或者调整),所以就慢下来了(相对于内存)
在海量数据的情况下,使用map的方式会对内存造成很大压力,容易被熔断,或者有OOM风险,更推荐使用默认的global ordinals;
eager_global_ordinals模式低写高查、数据量不大的index中使用:需要常驻内存,每次refresh以后就会重构,增大了内存以及cpu的消耗;
map模式:
例如:aaaa,bbbb,aaaa,cccc,aaaa 这样的字符串放到内存计算,结果就是
aaaa:3
bbbb:1
cccc:1
Global ordinals模式:
把aaaa映射成1,bbbb映射成2,cccc映射成3
然后计算就是 1,2,1,3,1
最后结果再转化成 aaaa,bbbb,cccc
更多可以参考官方:
https://www.elastic.co/guide/en/elasticsearch/reference/6.8/eager-global-ordinals.html