cartinality metric
cartinality metric:对每个bucket中的指定的field进行去重,取去重之后的count,类似于count(distinct)
案例
需求: 每月销售品牌的数量统计1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19GET tvs/sales/_search
{
"size": 0,
"aggs": {
"month": {
"date_histogram": {
"field": "sold_date",
"interval": "month"
},
"aggs": {
"distinct_brand": {
"cardinality": {
"field": "brand"
}
}
}
}
}
}
返回值:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100{
"took": 35,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 8,
"max_score": 0,
"hits": []
},
"aggregations": {
"month": {
"buckets": [
{
"key_as_string": "2016-05-01T00:00:00.000Z",
"key": 1462060800000,
"doc_count": 1,
"distinct_brand": {
"value": 1
}
},
{
"key_as_string": "2016-06-01T00:00:00.000Z",
"key": 1464739200000,
"doc_count": 0,
"distinct_brand": {
"value": 0
}
},
{
"key_as_string": "2016-07-01T00:00:00.000Z",
"key": 1467331200000,
"doc_count": 1,
"distinct_brand": {
"value": 1
}
},
{
"key_as_string": "2016-08-01T00:00:00.000Z",
"key": 1470009600000,
"doc_count": 1,
"distinct_brand": {
"value": 1
}
},
{
"key_as_string": "2016-09-01T00:00:00.000Z",
"key": 1472688000000,
"doc_count": 0,
"distinct_brand": {
"value": 0
}
},
{
"key_as_string": "2016-10-01T00:00:00.000Z",
"key": 1475280000000,
"doc_count": 1,
"distinct_brand": {
"value": 1
}
},
{
"key_as_string": "2016-11-01T00:00:00.000Z",
"key": 1477958400000,
"doc_count": 2,
"distinct_brand": {
"value": 1
}
},
{
"key_as_string": "2016-12-01T00:00:00.000Z",
"key": 1480550400000,
"doc_count": 0,
"distinct_brand": {
"value": 0
}
},
{
"key_as_string": "2017-01-01T00:00:00.000Z",
"key": 1483228800000,
"doc_count": 1,
"distinct_brand": {
"value": 1
}
},
{
"key_as_string": "2017-02-01T00:00:00.000Z",
"key": 1485907200000,
"doc_count": 1,
"distinct_brand": {
"value": 1
}
}
]
}
}
}
先根据月进行分组,然后用cardinality对品牌去重,就ok了
优化准确率和内存开销
precision_threshold
先来看个请求1
2
3
4
5
6
7
8
9
10
11
12GET /tvs/sales/_search
{
"size" : 0,
"aggs" : {
"distinct_brand" : {
"cardinality" : {
"field" : "brand",
"precision_threshold" : 100
}
}
}
}
请求中有一个参数 precision_threshold,值是100(默认的就是100),这个搜索请求是根据brand来去重的,precision_threshold的作用就是 如果brand的unique value(去重后的唯一值)在100个以内的话,几乎是100%的准确率的
- precision_threshold:在多少个unique value以内,cardinality会保证几乎100%准确
内存开销
cardinality算法会占用 precision_threshold 8byte 的内存消耗, 比如我们上面这个请求中设置的是100,那么他的内存消耗就是 100 8byte = 800byte ,占用的内存很小.
而且搜索结果中unique value 的数量如果的确在设置的precision_threshold值以内的话,是可以确保100%准确的
官方的说明是如果设置precision_threshold的值是100,实际的unique value有百万的话,错误率是5%以内
HyperLogLog++ (HLL)算法性能优化
cardinality运算的底层算法是HLL算法,如果要优化的话也是去优化HLL算法的性能
会对所有的uqniue value取hash值,通过hash值近似去求distcint count,所以会有一定的误差
默认情况下,发送一个cardinality请求的时候,会动态地对所有的field value取hash值, 我们可以在建立索引的时候,就对字段去设置他的hash值, 就不用去动态的取了
示例
1 | PUT /tvs/ |
索引建立好之后,查询的时候,就需要用brand.hash这个field1
2
3
4
5
6
7
8
9
10
11
12GET /tvs/sales/_search
{
"size" : 0,
"aggs" : {
"distinct_brand" : {
"cardinality" : {
"field" : "brand.hash",
"precision_threshold" : 100
}
}
}
}
这种方式其实性能也不会提升多少,可用可不用