在Elasticsearch中实现统计异常检测器——第二部分

Implementing a statistical anomaly detector in Elasticsearch - Part 2

  上一周,咱们创建了一个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

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

相关文章
相关标签/搜索