信息聚合系列之近似聚合
摘要
如果所有的數(shù)據(jù)都在一臺(tái)機(jī)器上,那么生活會(huì)容易許多,CS201 課商教的經(jīng)典算法就足夠應(yīng)付這些問題。但如果所有的數(shù)據(jù)都在一臺(tái)機(jī)器上,那么就不需要像 Elasticsearch 這樣的分布式軟件了。不過一旦我們開始分布式數(shù)據(jù)存儲(chǔ),算法的選擇就需務(wù)必小心。
版本
elasticsearch版本: elasticsearch-2.x
內(nèi)容
如果所有的數(shù)據(jù)都在一臺(tái)機(jī)器上,那么生活會(huì)容易許多,CS201 課商教的經(jīng)典算法就足夠應(yīng)付這些問題。但如果所有的數(shù)據(jù)都在一臺(tái)機(jī)器上,那么就不需要像 Elasticsearch 這樣的分布式軟件了。不過一旦我們開始分布式數(shù)據(jù)存儲(chǔ),算法的選擇就需務(wù)必小心。
有些算法可以分布執(zhí)行,到目前為止討論過的所有聚合都是單次請(qǐng)求獲得精確結(jié)果的。這些類型的算法通常是高度并行的,因?yàn)樗鼈儫o須任何額外代價(jià),就能在多臺(tái)機(jī)器上并行執(zhí)行。比如當(dāng)計(jì)算?max?度量時(shí),以下的算法就非常簡單:
請(qǐng)求廣播到所有分片。
查看每個(gè)文檔的?price?字段。如果?price > current_max,將?current_max?替換成?price。
返回所有分片的最大?price?并傳給協(xié)調(diào)節(jié)點(diǎn)。
找到從所有分片返回的最大?price?。這是最終的最大值。
這個(gè)算法可以隨著機(jī)器數(shù)的線性增長而橫向擴(kuò)展,無須任何協(xié)調(diào)操作(機(jī)器之間不需要討論中間結(jié)果),而且內(nèi)存消耗很?。ㄒ粋€(gè)整型就能代表最大值)。
不幸的是,不是所有的算法都像獲取最大值這樣簡單。更加復(fù)雜的操作則需要在算法的性能和內(nèi)存使用上做出權(quán)衡。對(duì)于這個(gè)問題,我們有個(gè)三角因子模型:大數(shù)據(jù)、精確性和實(shí)時(shí)性。
我們需要選擇其中兩項(xiàng):
精確 + 實(shí)時(shí)
數(shù)據(jù)可以存入單臺(tái)機(jī)器的內(nèi)存之中,我們可以隨心所欲,使用任何想用的算法。結(jié)果會(huì) 100% 精確,響應(yīng)會(huì)相對(duì)快速。
大數(shù)據(jù) + 精確
傳統(tǒng)的 Hadoop。可以處理 PB 級(jí)的數(shù)據(jù)并且為我們提供精確的答案,但它可能需要幾周的時(shí)間才能為我們提供這個(gè)答案。
大數(shù)據(jù) + 實(shí)時(shí)
近似算法為我們提供準(zhǔn)確但不精確的結(jié)果。
Elasticsearch 目前支持兩種近似算法(cardinality(基數(shù))?和?percentiles(百分位數(shù)))。它們會(huì)提供準(zhǔn)確但不是 100% 精確的結(jié)果。以犧牲一點(diǎn)小小的估算錯(cuò)誤為代價(jià),這些算法可以為我們換來高速的執(zhí)行效率和極小的內(nèi)存消耗。
對(duì)于大多數(shù)應(yīng)用領(lǐng)域,能夠?qū)崟r(shí)返回高度準(zhǔn)確的結(jié)果要比 100% 精確結(jié)果重要得多。乍一看這可能是天方夜譚。有人會(huì)叫“我們需要精確的答案!”。但仔細(xì)考慮 0.5% 錯(cuò)誤所帶來的影響:
99% 的網(wǎng)站延時(shí)都在 132ms 以下。
0.5% 的誤差對(duì)以上延時(shí)的影響在正負(fù) 0.66ms 。
近似計(jì)算會(huì)在毫秒內(nèi)放回結(jié)果,而“完全正確”的結(jié)果就可能需要幾秒,甚至無法返回。
只要簡單的查看網(wǎng)站的延時(shí)情況,難道我們會(huì)在意近似結(jié)果是在 132.66ms 內(nèi)返回而不是 132ms?當(dāng)然,不是所有的領(lǐng)域都能容忍這種近似結(jié)果,但對(duì)于絕大多數(shù)來說是沒有問題的。接受近似結(jié)果更多的是一種文化觀念上的壁壘而不是商業(yè)或技術(shù)上的需要。
查找唯一值的數(shù)目(Finding Distinct Counts)
Elasticsearch 提供的首個(gè)近似聚合是基數(shù)度量。它提供一個(gè)字段的基數(shù),即該字段的唯一值的數(shù)目。可能會(huì)對(duì) SQL 形式比較熟悉:
SELECTDISTINCT(color)FROMcars
Distinct 計(jì)數(shù)是一個(gè)普通的操作,可以回答很多基本的商業(yè)問題:
網(wǎng)站的獨(dú)立訪問用戶(UVs)是多少?
賣了多少種汽車?
每月有多少獨(dú)立用戶購買了商品?
我們可以用基數(shù)度量確定經(jīng)銷商銷售汽車顏色的種類:
GET/cars/transactions/_search? {"size":0,"aggs": {"distinct_colors": {"cardinality": {"field":"color"}? ? ? ? ? }? ? ? }? }
返回的結(jié)果表明已經(jīng)售賣了三種不同顏色的汽車:
..."aggregations": {"distinct_colors": {"value":3}? }...
可以讓我們的例子變得更有用:每月有多少顏色的車被售出?為了得到這個(gè)度量,我們只需要將一個(gè)?cardinality?度量嵌入一個(gè)?date_histogram:
GET/cars/transactions/_search? {"size":0,"aggs": {"months": {"date_histogram": {"field":"sold","interval":"month"},"aggs": {"distinct_colors": {"cardinality": {"field":"color"}? ? ? ? ? ? }? ? ? ? ? }? ? ? ? }? ? }? }
學(xué)會(huì)權(quán)衡(Understanding the Trade-offs)
正如我們本章開頭提到的,cardinality?度量是一個(gè)近似算法。它是基于?HyperLogLog++(HLL)算法的,HLL 會(huì)先對(duì)我們的輸入作哈希運(yùn)算,然后根據(jù)哈希運(yùn)算的結(jié)果中的 bits 做概率估算從而得到基數(shù)。
我們不需要理解技術(shù)細(xì)節(jié)(如果確實(shí)感興趣,可以閱讀這篇論文),但我們最好應(yīng)該關(guān)注一下這個(gè)算法的特性:
可配置的精度,用來控制內(nèi)存的使用(更精確 = 更多內(nèi)存)。
對(duì)于低基數(shù)集能夠達(dá)到高準(zhǔn)確度。
固定的內(nèi)存使用。即使有幾千或甚至上百億的唯一值,內(nèi)存的使用也只是依賴于配置里的精度要求。
要配置精度,我們必須指定?precision_threshold?參數(shù)的值。這個(gè)閥值定義了在何種基數(shù)水平下我們希望得到一個(gè)近乎精確的結(jié)果。參考以下示例:
GET/cars/transactions/_search? {"size":0,"aggs": {"distinct_colors": {"cardinality": {"field":"color","precision_threshold":100#1}? ? ? ? ? }? ? ? }? }
#1?precision_threshold?接受 0–40,000 之間的數(shù)字,更大的值還是會(huì)被當(dāng)作 40,000 來處理。
示例會(huì)確保當(dāng)字段唯一值在 100 以內(nèi)時(shí)會(huì)得到非常準(zhǔn)確的結(jié)果。盡管算法是無法保證這點(diǎn)的,但如果基數(shù)在閥值以下,幾乎總是 100% 正確的。高于閥值的基數(shù)會(huì)開始節(jié)省內(nèi)存而犧牲準(zhǔn)確度,同時(shí)也會(huì)對(duì)度量結(jié)果帶入誤差。
對(duì)于指定的閥值,HLL 的數(shù)據(jù)結(jié)構(gòu)會(huì)大概使用內(nèi)存?precision_threshold * 8?字節(jié),所以就必須在犧牲內(nèi)存和獲得額外的準(zhǔn)確度間做平衡。
在實(shí)際應(yīng)用中,100?的閥值可以在唯一值為百萬的情況下仍然將誤差維持 5% 以內(nèi)。
速度優(yōu)化(Optimizing for Speed)
如果想要獲得唯一數(shù)目的值,通常需要查詢整個(gè)數(shù)據(jù)集合(或幾乎所有數(shù)據(jù))。所有基于所有數(shù)據(jù)的操作都必須迅速,原因是顯然的。HyperLogLog 的速度已經(jīng)很快了,它只是簡單的對(duì)數(shù)據(jù)做哈希以及一些位操作。
但如果速度對(duì)我們至關(guān)重要,可以做進(jìn)一步的優(yōu)化,因?yàn)?HLL 只需要字段內(nèi)容的哈希值,我們可以在索引時(shí)就預(yù)先計(jì)算好。就能在查詢時(shí)跳過哈希計(jì)算然后將數(shù)據(jù)信息直接加載出來。
注意
預(yù)先計(jì)算哈希值只對(duì)內(nèi)容很長或者基數(shù)很高的字段有用,計(jì)算這些字段的哈希值的消耗在查詢時(shí)是無法忽略的。
盡管數(shù)值字段的哈希計(jì)算是非??焖俚?,存儲(chǔ)它們的原始值通常需要同樣(或更少)的內(nèi)存空間。這對(duì)低基數(shù)的字符串字段同樣適用,Elasticsearch 的內(nèi)部優(yōu)化能夠保證每個(gè)唯一值只計(jì)算一次哈希。
基本上說,預(yù)先計(jì)算并不能保證所有的字段都快,它只對(duì)那些具有高基數(shù)和內(nèi)容很長的字符串字段有作用。需要記住的是預(yù)先計(jì)算也只是簡單地將代價(jià)轉(zhuǎn)到索引時(shí),代價(jià)就在那里,不增不減。
To do this, we need to add a new multifield to our data. We’ll delete our index, add a new mapping that includes the hashed field, and then reindex:
要想這么做,我們需要為數(shù)據(jù)增加一個(gè)新的多值字段。我們先刪除索引,再增加一個(gè)包括哈希值字段的映射,然后重新索引:
DELETE/cars/PUT/cars/{"mappings": {"transactions": {"properties": {"color": {"type":"string","fields": {"hash": {"type":"murmur3"#1}? ? ? ? ? ? }? ? ? ? ? }? ? ? ? }? ? ? }? ? }? }? POST/cars/transactions/_bulk? {"index": {}}? {"price":10000,"color":"red","make":"honda","sold":"2014-10-28"}? {"index": {}}? {"price":20000,"color":"red","make":"honda","sold":"2014-11-05"}? {"index": {}}? {"price":30000,"color":"green","make":"ford","sold":"2014-05-18"}? {"index": {}}? {"price":15000,"color":"blue","make":"toyota","sold":"2014-07-02"}? {"index": {}}? {"price":12000,"color":"green","make":"toyota","sold":"2014-08-19"}? {"index": {}}? {"price":20000,"color":"red","make":"honda","sold":"2014-11-05"}? {"index": {}}? {"price":80000,"color":"red","make":"bmw","sold":"2014-01-01"}? {"index": {}}? {"price":25000,"color":"blue","make":"ford","sold":"2014-02-12"}
#1 多值字段的類型是?murmur3,這是一個(gè)哈希函數(shù)。
現(xiàn)在當(dāng)我們執(zhí)行聚合時(shí),我們使用?color.hash?字段而不是?color?字段:
GET/cars/transactions/_search? {"size":0,"aggs": {"distinct_colors": {"cardinality": {"field":"color.hash"#1}? ? ? ? ? }? ? ? }? }
#1 注意我們指定的是哈希過的多值字段,而不是原始字段。
現(xiàn)在?cardinality?度量會(huì)讀取?"color.hash"?里的值(預(yù)先計(jì)算的哈希值),并將它們作為原始值的動(dòng)態(tài)哈希值。
每個(gè)文檔節(jié)省的時(shí)間有限,但如果哈希每個(gè)字段需要 10 納秒而我們的聚合需要訪問一億文檔,那么每個(gè)查詢就需要多花 1 秒鐘的時(shí)間。如果我們發(fā)現(xiàn)自己在很多文檔都會(huì)使用?cardinality?基數(shù),可以做些性能分析看是否有必要在我們部署的應(yīng)用中采用預(yù)先計(jì)算哈希的方式。
百分計(jì)算(Calculating Percentiles)
Elasticsearch 提供的另外一個(gè)近似度量就是?percentiles?百分位數(shù)度量。百分位數(shù)展現(xiàn)某以具體百分比下觀察到的數(shù)值。例如,第95個(gè)百分位上的數(shù)值,是高于 95% 的數(shù)據(jù)總和。
百分位數(shù)通常用來找出異常。在(統(tǒng)計(jì)學(xué))的正態(tài)分布下,第 0.13 和 第 99.87 的百分位數(shù)代表與均值距離三倍標(biāo)準(zhǔn)差的值。任何處于三倍標(biāo)準(zhǔn)差之外的數(shù)據(jù)通常被認(rèn)為是不尋常的,因?yàn)樗c平均值相差太大。
更具體的說,假設(shè)我們正運(yùn)行一個(gè)龐大的網(wǎng)站,而我們的任務(wù)是保證用戶請(qǐng)求能得到快速響應(yīng),因此我們就需要監(jiān)控網(wǎng)站的延時(shí)來判斷響應(yīng)是否能達(dá)到目標(biāo)。
在此場(chǎng)景下,一個(gè)常用的度量方法就是平均響應(yīng)延時(shí),但這是一個(gè)不好的選擇(盡管很常用),因?yàn)槠骄鶖?shù)通常會(huì)隱藏那些異常值,中位數(shù)有著同樣的問題。我們可以嘗試最大值,但這個(gè)度量會(huì)輕而易舉的被單個(gè)異常值破壞。
在圖 Figure 40, “Average request latency over time” 查看問題。如果我們倚靠如平均值或中位數(shù)這樣的簡單度量,就會(huì)得到像這樣一幅圖 Figure 40, “Average request latency over time”.
Figure 40. Average request latency over time

一切正常。圖上有輕微的波動(dòng),但沒有什么值得關(guān)注的。但如果我們加載 99 百分位數(shù)時(shí)(這個(gè)值代表最慢的 1% 的延時(shí)),我們看到了完全不同的一幅畫面,如圖Figure 41, “Average request latency with 99th percentile over time” 所示。
Figure 41. Average request latency with 99th percentile over time

令人吃驚!在上午九點(diǎn)半時(shí),均值只有 75ms。如果作為一個(gè)系統(tǒng)管理員,我們都不會(huì)看他第二眼。一切正常!但 99 百分位告訴我們有 1% 的用戶碰到的延時(shí)超過 850ms,這是另外一幅場(chǎng)景。在上午4點(diǎn)48時(shí)也有一個(gè)小波動(dòng),這甚至無法從平均值和中位數(shù)曲線上觀察到。
這只是百分位的一個(gè)應(yīng)用場(chǎng)景,百分位還可以被用來快速用肉眼觀察數(shù)據(jù)的分布,檢查是否有數(shù)據(jù)傾斜或雙峰甚至更多。
百分位度量(Percentile Metric)
讓我加載一個(gè)新的數(shù)據(jù)集(汽車的數(shù)據(jù)不太適用于百分位)。我們要索引一系列網(wǎng)站延時(shí)數(shù)據(jù)然后運(yùn)行一些百分位操作進(jìn)行查看:
POST/website/logs/_bulk? {"index": {}}? {"latency":100,"zone":"US","timestamp":"2014-10-28"}? {"index": {}}? {"latency":80,"zone":"US","timestamp":"2014-10-29"}? {"index": {}}? {"latency":99,"zone":"US","timestamp":"2014-10-29"}? {"index": {}}? {"latency":102,"zone":"US","timestamp":"2014-10-28"}? {"index": {}}? {"latency":75,"zone":"US","timestamp":"2014-10-28"}? {"index": {}}? {"latency":82,"zone":"US","timestamp":"2014-10-29"}? {"index": {}}? {"latency":100,"zone":"EU","timestamp":"2014-10-28"}? {"index": {}}? {"latency":280,"zone":"EU","timestamp":"2014-10-29"}? {"index": {}}? {"latency":155,"zone":"EU","timestamp":"2014-10-29"}? {"index": {}}? {"latency":623,"zone":"EU","timestamp":"2014-10-28"}? {"index": {}}? {"latency":380,"zone":"EU","timestamp":"2014-10-28"}? {"index": {}}? {"latency":319,"zone":"EU","timestamp":"2014-10-29"}
數(shù)據(jù)有三個(gè)值:延時(shí)、數(shù)據(jù)中心的區(qū)域以及時(shí)間戳。讓我們對(duì)數(shù)據(jù)全集執(zhí)行百分位操作以獲得數(shù)據(jù)分布情況的直觀感受:
GET/website/logs/_search? {"size":0,"aggs": {"load_times": {"percentiles": {"field":"latency"#1}? ? ? ? ? },"avg_load_time": {"avg": {"field":"latency"#2}? ? ? ? ? }? ? ? }? }
#1?percentiles?度量被應(yīng)用到?latency?延時(shí)字段。
#2 為了比較,我們對(duì)相同字段使用?avg?度量。
默認(rèn)情況下,percentiles?度量會(huì)返回一組預(yù)定義的百分位數(shù)值:[1, 5, 25, 50, 75, 95, 99]。它們表示了人們感興趣的常用百分位數(shù)值,極端的百分位數(shù)在范圍的兩邊,其他的一些處于中部。在返回的響應(yīng)中,我們可以看到最小延時(shí)在 75ms 左右,而最大延時(shí)差不多有 600ms。與之形成對(duì)比的是,平均延時(shí)在 200ms 左右,信息并不是很多:
..."aggregations": {"load_times": {"values": {"1.0":75.55,"5.0":77.75,"25.0":94.75,"50.0":101,"75.0":289.75,"95.0":489.34999999999985,"99.0":596.2700000000002}? ? },"avg_load_time": {"value":199.58333333333334}? }
所以顯然延時(shí)的分布很廣,然我們看看它們是否與數(shù)據(jù)中心的地理區(qū)域有關(guān):
GET/website/logs/_search? {"size":0,"aggs": {"zones": {"terms": {"field":"zone"#1},"aggs": {"load_times": {"percentiles": {#2"field":"latency","percents": [50,95.0,99.0]#3}? ? ? ? ? ? ? ? ? },"load_avg": {"avg": {"field":"latency"}? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? }? ? ? ? ? }? ? ? }? }
#1 首先根據(jù)區(qū)域我們將延時(shí)分到不同的桶中。
#2 再計(jì)算每個(gè)區(qū)域的百分位數(shù)值。
#3?percents?參數(shù)接受了我們想返回的一組百分位數(shù),因?yàn)槲覀冎粚?duì)長的延時(shí)感興趣。
在響應(yīng)結(jié)果中,我們發(fā)現(xiàn)歐洲區(qū)域(EU)要比美國區(qū)域(US)慢很多,在美國區(qū)域(US),50 百分位與 99 百分位十分接近,它們都接近均值。
與之形成對(duì)比的是,歐洲區(qū)域(EU)在 50 和 99 百分位有較大區(qū)分。現(xiàn)在,顯然可以發(fā)現(xiàn)是歐洲區(qū)域(EU)拉低了延時(shí)的統(tǒng)計(jì)信息,我們知道歐洲區(qū)域的 50% 延時(shí)都在 300ms+。
..."aggregations": {"zones": {"buckets": [? ? ? ? ? {"key":"eu","doc_count":6,"load_times": {"values": {"50.0":299.5,"95.0":562.25,"99.0":610.85}? ? ? ? ? ? },"load_avg": {"value":309.5}? ? ? ? ? },? ? ? ? ? {"key":"us","doc_count":6,"load_times": {"values": {"50.0":90.5,"95.0":101.5,"99.0":101.9}? ? ? ? ? ? },"load_avg": {"value":89.66666666666667}? ? ? ? ? }? ? ? ]? ? }? }? ...
百分位等級(jí)(Percentile Ranks)
這里有另外一個(gè)緊密相關(guān)的度量叫?percentile_ranks。百分位度量告訴我們落在某個(gè)百分比以下的所有文檔的最小值。例如,如果 50 百分位是 119ms,那么有 50% 的文檔數(shù)值都不超過 119ms。percentile_ranks?告訴我們某個(gè)具體值屬于哪個(gè)百分位。119ms 的?percentile_ranks?是在 50 百分位。這基本是個(gè)雙向關(guān)系,例如:
50 百分位是 119ms。
119ms 百分位等級(jí)是 50 百分位。
所以假設(shè)我們網(wǎng)站必須維持的服務(wù)等級(jí)協(xié)議(SLA)是響應(yīng)時(shí)間低于 210ms。然后,開個(gè)玩笑,我們老板警告我們?nèi)绻憫?yīng)時(shí)間超過 800ms 會(huì)把我開除??梢岳斫獾氖牵覀兿M烙卸嗌侔俜直鹊恼?qǐng)求可以滿足 SLA 的要求(并期望至少在 800ms 以下?。?。
為了做到這點(diǎn),我們可以應(yīng)用?percentile_ranks?度量而不是?percentiles?度量:
GET/website/logs/_search? {"size":0,"aggs": {"zones": {"terms": {"field":"zone"},"aggs": {"load_times": {"percentile_ranks": {"field":"latency","values": [210,800]#1}? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? }? ? ? ? ? }? ? ? }? }
#1?percentile_ranks?度量接受一組我們希望分級(jí)的數(shù)值。
在聚合運(yùn)行后,我們能得到兩個(gè)值:
"aggregations": {"zones": {"buckets": [? ? ? ? ? {"key":"eu","doc_count":6,"load_times": {"values": {"210.0":31.944444444444443,"800.0":100}? ? ? ? ? ? }? ? ? ? ? },? ? ? ? ? {"key":"us","doc_count":6,"load_times": {"values": {"210.0":100,"800.0":100}? ? ? ? ? ? }? ? ? ? ? }? ? ? ]? ? }? }
這告訴我們?nèi)c(diǎn)重要的信息:
在歐洲(EU),210ms 的百分位等級(jí)是 31.94% 。
在美國(US),210ms 的百分位等級(jí)是 100% 。
在歐洲(EU)和美國(US),800ms 的百分位等級(jí)是 100% 。
通俗的說,在歐洲區(qū)域(EU)只有 32% 的響應(yīng)時(shí)間滿足服務(wù)等級(jí)協(xié)議(SLA),而美國區(qū)域(US)始終滿足服務(wù)等級(jí)協(xié)議的。但幸運(yùn)的是,兩個(gè)區(qū)域所有響應(yīng)時(shí)間都在 800ms 以下,所有我們還不會(huì)被炒魷魚(至少目前不會(huì))。
percentile_ranks?度量提供了與?percentiles?相同的信息,但它以不同方式呈現(xiàn),如果我們對(duì)某個(gè)具體數(shù)值更關(guān)心,使用它會(huì)更方便。
學(xué)會(huì)權(quán)衡(Understanding the Trade-offs)
和基數(shù)一樣,計(jì)算百分位需要一個(gè)近似算法。樸素的實(shí)現(xiàn)會(huì)維護(hù)一個(gè)所有值的有序列表,但當(dāng)我們有幾十億數(shù)據(jù)分布在幾十個(gè)節(jié)點(diǎn)時(shí),這幾乎是不可能的。
取而代之的是?percentiles?使用一個(gè) TDigest 算法(由 Ted Dunning 在?Computing Extremely Accurate Quantiles Using T-Digests?里面提出的)。有了 HyperLogLog,就不需要理解完整的技術(shù)細(xì)節(jié),但有必要了解算法的特性:
百分位的準(zhǔn)確度與百分位的極端程度相關(guān),也就是說 1 或 99 的百分位要比 50 百分位要準(zhǔn)確。這只是數(shù)據(jù)結(jié)構(gòu)內(nèi)部機(jī)制的一種特性,但這是一個(gè)好的特性,因?yàn)槎鄶?shù)人只關(guān)心極端的百分位。
對(duì)于數(shù)值集合較小的情況,百分位非常準(zhǔn)確。如果數(shù)據(jù)集足夠小,百分位可能 100% 精確。
隨著桶里數(shù)值的增長,算法會(huì)開始對(duì)百分位進(jìn)行估算。它能有效在準(zhǔn)確度和內(nèi)存節(jié)省之間做出權(quán)衡。不準(zhǔn)確的程度比較難以總結(jié),因?yàn)樗蕾囉诰酆蠒r(shí)數(shù)據(jù)的分布以及數(shù)據(jù)量的大小。
與基數(shù)類似,我們可以通過修改參數(shù)?compression?來控制內(nèi)存與準(zhǔn)確度之間的比值。
TDigest 算法用節(jié)點(diǎn)近似計(jì)算百分比:節(jié)點(diǎn)越多,準(zhǔn)確度越高(同時(shí)內(nèi)存消耗也越大),這都與數(shù)據(jù)量成正比。compression?參數(shù)限制節(jié)點(diǎn)的最大數(shù)目為?20 * compression。
因此,通過增加壓縮比值,我們可以提高準(zhǔn)確度同時(shí)也消耗更多內(nèi)存。更大的壓縮比值會(huì)使算法運(yùn)行更慢,因?yàn)榈讓拥臉湫螖?shù)據(jù)結(jié)構(gòu)的存儲(chǔ)也會(huì)增長,也導(dǎo)致操作的代價(jià)更高。默認(rèn)的壓縮比值是?100。
一個(gè)節(jié)點(diǎn)粗略計(jì)算使用 32 字節(jié)的內(nèi)存,所以在最壞的情況下(例如,大量數(shù)據(jù)有序存入),默認(rèn)設(shè)置會(huì)生成一個(gè)大小約為 64KB 的 TDigest。在實(shí)際應(yīng)用中,數(shù)據(jù)會(huì)更隨機(jī),所以 TDigest 使用的內(nèi)存會(huì)更少。
被ES的聚合效率折騰的沒脾氣,轉(zhuǎn)幾篇文章看看