在Elasticsearch中實現統計異常檢測器——第二部分


Implementing a statistical anomaly detector in Elasticsearch - Part 2

  上一周,我們建立了一個pipeline聚合,將數千個數據點分解成少數代表性指標。 這形成了Atlas的基礎,並且為實現異常檢測器所做的所有重大工作。本周,我們將結束實施並生成一些有趣的圖表。

  我們創建的聚合被設計為在特定的時間窗口上運行:給定日期范圍,它將為每一個metric發出第90個百分點的意外(surprise)值。要完全實現Atlas,我們需要隨着時間的推移繪制第90個百分點值。目前這個功能僅僅使用Pipeline聚合是不可能使用的(雖然已經提出了一種“滑動柱狀圖”功能來彌補差距)。

  替代的,我們將把責任移交給TimeLion,它非常適合這種后期處理(Timelion是一個新的{Re}search項目,在kibana內部進行流暢的時間序列操作,你可以在這里閱讀更多)。

  

  假如你重新去看模擬器的代碼,你將看到我們在數據生成之后運行了一查詢系列 。我們以一小時的增加滑動我們的Pipeline聚合數據(窗口的大小為24小時)。我們還使用了filter_path來最小化輸出,我們實際並不關心60,000個buckets。我們僅僅想要每個metric的“ninetieth_surprise”。過濾響應大大較少了網絡傳輸。然后將值索引回Elasticsearch,以便我們稍后再對其進行統計。

  我們在模擬器中提前預處理了這些值,以簡化演示,但在一個真實的系統中,你可能會有一個Watcher或者cronjob每小時執行一次查詢並保存結果。

Plotting 90th percentile surprise

  通過上周的艱難舉措,我們可以轉而使用Timelion完成實施。第一個業務是降低特定指標的第90個值(the 90th values)。我們可以使用以下Timelion語法:

.es('metric:0', metric='avg:value').label("#0 90th surprise")

  它將生成看起來像這樣的一張圖表:

  

  那看起來很有趣!絕對有事情發生。我們來看看這張圖表的含義,因為它是Atlas的工作原理:

  • 上周,我們計算了每一個時間序列的“surprise”:偏離自己的移動平均線。
  • 然后,我們收集了這些“surprise”值的前第90個百分位數,並且正在隨着時間的推移來繪制它們。
  • 實際上,這張圖表顯示告訴我們前面"surprise"(偏差)的變化性。
  • 這個圖標中大幅度的顛簸意味着數據變得更加 surprising,前第90個百分位數發生了巨大變化(上升或下降,因為我們使用絕對值計算surprise)

   實際上,如果我們看到一個凸起,我們可以得出結論,基礎數據已經發生了改變,以改變了我們的正常方差,可能是由於中斷。這是Atlas的核心:不要看你的數據,因為它是如此的多。相反,觀察偏離平均值的第90個百分位數的差異。

  假如將上圖表和metric #0的實際數據相比較,你將看到明顯的區別:

  

Building the Atlas Dashboard

  當然,訣竅是現在自動識別那些凸起和圖表/警告。讓我們開始構件邏輯。當第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的圖表,以及顯示中斷創建時的圖表(你顯然不會在生產環境中使用,但對於驗證我們的模擬數據是有用的):

 

Analysis of anomalies

  如果你通過中斷圖表(左上角)進行操作,你將至少在一個metric圖表中找到相關的異常,通常幾個在同時。令人鼓舞的是,異常被標記為所有類型的中斷(node,query,metric)。注腳包含了一個中斷的列表和它們的大小,以讓你了解影響。例如,一個“Query Disruption”持續了三個小時並且僅僅影響總共500個查詢中的12個(2.4%)。

  在圖表中看到的一個現象是一小段時間保持在高位的凸起。這部分是由於中斷的持續時間,有些持續了幾個小時。但也有可能是由於我們上周提到的pipeline聚合的局限性:我們選擇了每個時間序列最大surprise,而不是最后的surprise。這意味着在最壞的情況下,中斷會延長額外的24小時,因為一旦中斷從窗口上脫落,surprise才會重置。這完全依賴於選擇窗口的大小,並且可以通過增加/減少窗口來改變敏感度。

  這種現象不會影響的異常檢測,盡管如果你嘗試使用更長時間窗口,這一點變得更加明顯。一旦pipeline聚合有選擇“最后”的能力,這個現象應該就被解決了。

Conclusion

  So,那就是Atlas,在eBay建立的一個非常簡單--但非常有效--統計異常檢測系統,現在在Elasticsearch +Timelion上實現了。在pipeline聚合之前,這可能是由很多客戶端邏輯實現的。但是,每小時將60K的buckets流向客戶端處理的前景並不誘人,pipeline聚合已經將重要的舉措轉移到服務器以進行更有效的處理。

  pipeline聚合還很年輕,隨着時間的推移,期待更多功能被添加。假如你有一個難以在pipeline中表達的用例,請告訴我們!

The end! Or is it...

  “可是,等等” 你說,“這只是繪制異常,我如何獲取預警”。對於這個答案,你必須等到下周,當我們實現了TimeLion語法作為觀察者觀察,如此你能獲得email,Slack等等的自動預警,下周見!

Footnotes

  • 自定義movingstd()和showifgreater() Timelion 功能能在這里找到,關閉Kibana,把該功能增加到Timelion的源碼(kibana/installedPlugins/timelion/series_functions/<function_name>.js),刪除優化的包(rm kibana/optimize/bundles/timelion.bundle.js)並重啟Kibana。該功能現在應該可以在Timelion中使用了。注意:JavaScript不是我的特長,所以這些都不是典型的代碼示例。
  • 模擬中斷如下。格式為格式:中斷類型:開始時間-截止時間[影響 節點/查詢/指標]
    • Metric Disruption: 505-521 [0, 3, 4]
    • Metric Disruption: 145-151 [3, 4]
    • Node Disruption: 279-298 [0]
    • Metric Disruption: 240-243 [1]
    • Query Disruption: 352-355 [5, 23, 27, 51, 56, 64, 65, 70, 72, 83, 86, 95, 97, 116, 135, 139, 181, 185, 195, 200, 206, 231, 240, 263, 274, 291, 295, 307, 311, 315, 322, 328, 337, 347, 355, 375, 385, 426, 468]
    • Metric Disruption: 172-181 [0, 2]
    • Node Disruption: 334-337 [0]
    • Query Disruption: 272-275 [63, 64, 168, 179, 193, 204, 230, 295, 308, 343, 395, 458]
  • 在上周的文章之后,有一些關於數據本質的問題:也就是,使用正態(高斯)曲線生成數據。Atlas可以使用曲解的數據,因為現實生活中很多數據不會遵循一個很好的正態分布?我使用LogNormal曲線運行了一個快速測試,該曲線嚴重偏向左側,而Atlas依舊運行良好。該Atlas論文證實了這一實驗性證據。Atlas依賴於第90個surprise隨着時間推移而變得正常,即使基礎數據嚴重偏離,似乎也是如此。可能有關於Atlas在不同數據分布下行為的后續文章,假如我有時間進行實驗。

原文地址:https://www.elastic.co/blog/implementing-a-statistical-anomaly-detector-part-2


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM