cardinality度量是一個近似算法。它是基于HyperLogLog++(HLL)算法的。HLL 會先對我們的輸入作哈希運(yùn)算改鲫,然后根據(jù)哈希運(yùn)算的結(jié)果中的 bits 做概率估算從而得到基數(shù)肮街。
我們不需要理解技術(shù)細(xì)節(jié)(如果確實感興趣,可以閱讀這篇論文)饮笛,但我們最好應(yīng)該關(guān)注一下這個算法的特性:
可配置的精度咨察,用來控制內(nèi)存的使用(更精確 = 更多內(nèi)存)。
小的數(shù)據(jù)集精度是非常高的福青。
我們可以通過配置參數(shù)摄狱,來設(shè)置去重需要的固定內(nèi)存使用量。無論數(shù)千還是數(shù)十億的唯一值无午,內(nèi)存使用量只與你配置的精確度相關(guān)媒役。
要配置精度,我們必須指定precision_threshold參數(shù)的值宪迟。這個閾值定義了在何種基數(shù)水平下我們希望得到一個近乎精確的結(jié)果酣衷。參考以下示例:
precision_threshold接受 0–40,000 之間的數(shù)字,更大的值還是會被當(dāng)作 40,000 來處理次泽。
示例會確保當(dāng)字段唯一值在 100 以內(nèi)時會得到非常準(zhǔn)確的結(jié)果穿仪。盡管算法是無法保證這點(diǎn)的,但如果基數(shù)在閾值以下箕憾,幾乎總是 100% 正確的牡借。高于閾值的基數(shù)會開始節(jié)省內(nèi)存而犧牲準(zhǔn)確度,同時也會對度量結(jié)果帶入誤差袭异。
對于指定的閾值钠龙,HLL 的數(shù)據(jù)結(jié)構(gòu)會大概使用precision_threshold * 8字節(jié)的內(nèi)存,所以就必須在犧牲內(nèi)存和獲得額外的準(zhǔn)確度間做平衡御铃。
在實際應(yīng)用中碴里,100的閾值可以在唯一值為百萬的情況下仍然將誤差維持 5% 以內(nèi)。
上面是官方文檔的描述
實際上上真,我的請求測試咬腋,當(dāng)文檔很多的時候,這個誤差還是很可觀的:
Req
POST /xxx/_search
{
"size" : 0,
"aggs" : {
"distinc_count" : {
"cardinality" : {
"field" : "xx.keyword",
"precision_threshold": 100
}
}
}
}
Resp
{
"took": 1,
"timed_out": false,
"_shards": {
"total": 2,
"successful": 2,
"failed": 0
},
"hits": {
"total": 570470,
"max_score": 0,
"hits": []
},
"aggregations": {
"distinc_count": {
"value": 11158
}
}
}
Req
POST /xxx/_search
{
"size" : 0,
"aggs" : {
"distinc_count" : {
"cardinality" : {
"field" : "xx.keyword",
"precision_threshold": 10000
}
}
}
}
Resp
{
"took": 3,
"timed_out": false,
"_shards": {
"total": 2,
"successful": 2,
"failed": 0
},
"hits": {
"total": 570470,
"max_score": 0,
"hits": []
},
"aggregations": {
"distinc_count": {
"value": 10736
}
}
}
提高了precision_threshold睡互,得到高的精度估算精度根竿,耗時當(dāng)然也提高了點(diǎn)(因為之前請求過陵像,所以有緩存,這么低的時延寇壳,正常應(yīng)該幾十毫秒)醒颖。精度到10000,就基本沒誤差了壳炎。