上一週,咱們創建了一個pipeline聚合,將數千個數據點分解成少數表明性指標。 這造成了Atlas的基礎,而且爲實現異常檢測器所作的全部重大工做。本週,咱們將結束實施並生成一些有趣的圖表。html
咱們建立的聚合被設計爲在特定的時間窗口上運行:給定日期範圍,它將爲每個metric發出第90個百分點的意外(surprise)值。要徹底實現Atlas,咱們須要隨着時間的推移繪製第90個百分點值。目前這個功能僅僅使用Pipeline聚合是不可能使用的(雖然已經提出了一種「滑動柱狀圖」功能來彌補差距)。java
替代的,咱們將把責任移交給TimeLion,它很是適合這種後期處理(Timelion是一個新的{Re}search項目,在kibana內部進行流暢的時間序列操做,你能夠在這裏閱讀更多)。node
假如你從新去看模擬器的代碼,你將看到咱們在數據生成以後運行了一查詢系列 。咱們以一小時的增長滑動咱們的Pipeline聚合數據(窗口的大小爲24小時)。咱們還使用了filter_path來最小化輸出,咱們實際並不關心60,000個buckets。咱們僅僅想要每一個metric的「ninetieth_surprise」。過濾響應大大較少了網絡傳輸。而後將值索引回Elasticsearch,以便咱們稍後再對其進行統計。git
咱們在模擬器中提早預處理了這些值,以簡化演示,但在一個真實的系統中,你可能會有一個Watcher或者cronjob每小時執行一次查詢並保存結果。github
經過上週的艱難舉措,咱們能夠轉而使用Timelion完成實施。第一個業務是下降特定指標的第90個值(the 90th values)。咱們可使用如下Timelion語法:服務器
.es('metric:0', metric='avg:value').label("#0 90th surprise")
它將生成看起來像這樣的一張圖表:網絡
那看起來頗有趣!絕對有事情發生。咱們來看看這張圖表的含義,由於它是Atlas的工做原理:函數
實際上,若是咱們看到一個凸起,咱們能夠得出結論,基礎數據已經發生了改變,以改變了咱們的正常方差,多是因爲中斷。這是Atlas的核心:不要看你的數據,由於它是如此的多。相反,觀察偏離平均值的第90個百分位數的差別。測試
假如將上圖表和metric #0的實際數據相比較,你將看到明顯的區別:優化
固然,訣竅是如今自動識別那些凸起和圖表/警告。讓咱們開始構件邏輯。當第90個百分位數surprise是移動平均線以上3個標準差時,Atlas報警。假如你分解該問題,你將看到一些必要的組件:
首先,咱們構造滾動三標準差。咱們經過自定義movingstd()函數來作到這一點(參見注腳腳本,它與movingavg()函數基本相同),而後乘以3,以獲得第三個sigma:
注意:我縮進了全部的查詢,以使他們更加容易閱讀。
.es('metric:0', metric='avg:value') .movingstd(6) .multiply(3)
其次,我寫了一個計算數據自己滾動平均線的片斷:
.es('metric:0', metric='avg:value') .movingaverage(6)
最後,咱們經過將這兩個片斷加在一塊兒以建立「閾值」。這將建立一條在數據移動平均線以上三個標準差的線。
.es('metric:0', metric='avg:value') .movingaverage(6) .sum( .es('metric:0', metric='avg:value') .movingstd(6) .multiply(3) )
如今咱們有了一個「閾值」,咱們能夠用原始數據繪製,並看看它們若是比較:
嗯,OK。若是閾值是否工做,如今還不清楚。該圖表很難閱讀,一旦surprise值凸起,就會致使閾值的後續的凸起。這是由於凸起致使方差的巨大變化,滾動標準方差會上升,致使閾值自己的上升。
假如咱們放大第一個凸起,咱們能夠看到,在滾動標準方差上升以前,第90個百分位數稍微超過閾值:
(抱歉,此圖表錯誤標註:「metric:0」應該顯示爲「#0 Threshold」)
如今很清楚:咱們想顯示的是surprise超過閾值的時刻,而且另外忽略閾值(由於它只在第一瞬間有用)。當它超過閾值的時候,讓咱們顯示單獨的條,以替代持續的線條。
爲了作它,咱們構造了showifgreater()方法。這將只顯示第一個系列中的數據點,若是它們大於第二個系列中的數據點(參見注腳腳本)。
.es('metric:0', metric='avg:value').showifgreater(...)
要完成咱們的查詢,咱們僅僅但願顯示大於三個標準方差大的數據(假如它突破了閾值),而後咱們要顯示爲棒而不是線條。這組成了咱們最後的查詢:
.es('metric:0', metric='avg:value') .showifgreater( .es('metric:0', metric='avg:value') .movingaverage(6) .sum( .es('metric:0', metric='avg:value') .movingstd(6) .multiply(3) ) ).bars() .yaxis(2) .label("#0 anomalies")
這產生了更好看的圖表:
最後讓咱們加回數據自己,這樣就能夠進行比較了:
.es('metric:0', metric='avg:value') .label("#0 90th surprise"), .es('metric:0', metric='avg:value') .showifgreater( .es('metric:0', metric='avg:value') .movingaverage(6) .sum( .es('metric:0', metric='avg:value') .movingstd(6) .multiply(3) ) ).bars() .yaxis(2) .label("#0 anomalies")
瞧!咱們已經實現了Atlas!完整的面板包括每一個metric的圖表,以及顯示中斷建立時的圖表(你顯然不會在生產環境中使用,但對於驗證咱們的模擬數據是有用的):
若是你經過中斷圖表(左上角)進行操做,你將至少在一個metric圖表中找到相關的異常,一般幾個在同時。使人鼓舞的是,異常被標記爲全部類型的中斷(node,query,metric)。註腳包含了一箇中斷的列表和它們的大小,以讓你瞭解影響。例如,一個「Query Disruption」持續了三個小時而且僅僅影響總共500個查詢中的12個(2.4%)。
在圖表中看到的一個現象是一小段時間保持在高位的凸起。這部分是因爲中斷的持續時間,有些持續了幾個小時。但也有多是因爲咱們上週提到的pipeline聚合的侷限性:咱們選擇了每一個時間序列最大surprise,而不是最後的surprise。這意味着在最壞的狀況下,中斷會延長額外的24小時,由於一旦中斷從窗口上脫落,surprise纔會重置。這徹底依賴於選擇窗口的大小,而且能夠經過增長/減小窗口來改變敏感度。
這種現象不會影響的異常檢測,儘管若是你嘗試使用更長時間窗口,這一點變得更加明顯。一旦pipeline聚合有選擇「最後」的能力,這個現象應該就被解決了。
So,那就是Atlas,在eBay創建的一個很是簡單--但很是有效--統計異常檢測系統,如今在Elasticsearch +Timelion上實現了。在pipeline聚合以前,這多是由不少客戶端邏輯實現的。可是,每小時將60K的buckets流向客戶端處理的前景並不誘人,pipeline聚合已經將重要的舉措轉移到服務器以進行更有效的處理。
pipeline聚合還很年輕,隨着時間的推移,期待更多功能被添加。假如你有一個難以在pipeline中表達的用例,請告訴咱們!
「但是,等等」 你說,「這只是繪製異常,我如何獲取預警」。對於這個答案,你必須等到下週,當咱們實現了TimeLion語法做爲觀察者觀察,如此你能得到email,Slack等等的自動預警,下週見!
原文地址:https://www.elastic.co/blog/implementing-a-statistical-anomaly-detector-part-2