你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

教程:在 Azure Monitor 中使用 KQL 机器学习功能检测和分析异常

Kusto 查询语言 (KQL) 包括用于时序分析、异常情况检测、预测和根本原因分析的机器学习运算符、函数和插件。 可以在 Azure Monitor 中使用这些 KQL 功能执行高级数据分析,而不会产生将数据导出到外部机器学习工具所带来的开销。

本教程介绍如何执行下列操作:

  • 创建时序
  • 识别时序中的异常
  • 微调异常情况检测设置以细化结果
  • 分析异常的根本原因

注意

本教程提供了一个可在其中运行 KQL 查询示例的 Log Analytics 演示环境的链接。 演示环境中的数据是动态的,因此查询结果与本文中显示的查询结果不同。 但是,可以在自己的环境和所有使用 KQL 的 Azure Monitor 工具中实现相同的 KQL 查询和主体。

先决条件

  • 具有活动订阅的 Azure 帐户。 免费创建帐户
  • 包含日志数据的工作区。

所需的权限

你必须对查询的 Log Analytics 工作区具有 Microsoft.OperationalInsights/workspaces/query/*/read 权限,例如,Log Analytics 读者内置角色所提供的权限。

创建时序

使用 KQL make-series 运算符创建时序。

让我们根据使用情况表中的日志创建一个时序。该表保存有关工作区中的每个表每小时引入多少数据(包括可计费和不可计费数据)的信息。

此查询使用 make-series 来绘制工作区中每个表在过去 21 天内每天引入的可计费数据总量的图表:

单击以运行查询

let starttime = 21d; // The start date of the time series, counting back from the current date
let endtime = 0d; // The end date of the time series, counting back from the current date
let timeframe = 1d; // How often to sample data
Usage // The table we’re analyzing
| where TimeGenerated between (startofday(ago(starttime))..startofday(ago(endtime))) // Time range for the query, beginning at 12:00 AM of the first day and ending at 12:00 AM of the last day in the time range
| where IsBillable == "true" // Include only billable data in the result set
| make-series ActualUsage=sum(Quantity) default = 0 on TimeGenerated from startofday(ago(starttime)) to startofday(ago(endtime)) step timeframe by DataType // Creates the time series, listed by data type 
| render timechart // Renders results in a timechart

在生成的图表中,可以清楚地看到一些异常情况 - 例如,在 AzureDiagnosticsSecurityEvent 数据类型中:

显示工作区中每个表在过去 21 天内每天引入的数据总量图表的 GIF 动画。光标移动以突出显示图表上的三个使用异常。

接下来,我们将使用 KQL 函数列出时序中的所有异常。

注意

有关 make-series 语法和用法的详细信息,请参阅 make-series 运算符

查找时序中的异常

series_decompose_anomalies() 函数将一系列值用作输入并提取异常。

让我们将时序查询的结果集作为输入提供给 series_decompose_anomalies() 函数:

单击以运行查询

let starttime = 21d; // Start date for the time series, counting back from the current date
let endtime = 0d; // End date for the time series, counting back from the current date
let timeframe = 1d; // How often to sample data
Usage // The table we’re analyzing
| where TimeGenerated between (startofday(ago(starttime))..startofday(ago(endtime))) // Time range for the query, beginning at 12:00 AM of the first day and ending at 12:00 AM of the last day in the time range
| where IsBillable == "true" // Includes only billable data in the result set
| make-series ActualUsage=sum(Quantity) default = 0 on TimeGenerated from startofday(ago(starttime)) to startofday(ago(endtime)) step timeframe by DataType // Creates the time series, listed by data type
| extend(Anomalies, AnomalyScore, ExpectedUsage) = series_decompose_anomalies(ActualUsage) // Scores and extracts anomalies based on the output of make-series 
| mv-expand ActualUsage to typeof(double), TimeGenerated to typeof(datetime), Anomalies to typeof(double),AnomalyScore to typeof(double), ExpectedUsage to typeof(long) // Expands the array created by series_decompose_anomalies()
| where Anomalies != 0  // Returns all positive and negative deviations from expected usage
| project TimeGenerated,ActualUsage,ExpectedUsage,AnomalyScore,Anomalies,DataType // Defines which columns to return 
| sort by abs(AnomalyScore) desc // Sorts results by anomaly score in descending ordering

此查询返回过去三周内所有表的所有使用异常:

表的屏幕截图,其中显示了工作区中所有表的使用异常列表。

查看查询结果,可以看到该函数执行了以下操作:

  • 计算每个表的预期每日使用量。
  • 将实际每日使用量与预期使用量进行比较。
  • 为每个数据点分配一个异常分数,指示实际使用量与预期使用量的偏差程度。
  • 标识每个表中的正 (1) 和负 (-1) 异常。

注意

有关 series_decompose_anomalies() 语法和用法的详细信息,请参阅 series_decompose_anomalies()

微调异常情况检测设置以细化结果

最好查看初始查询结果,并在必要时对查询进行微调。 输入数据中的离群值可能影响函数的学习,你可能需要调整函数的异常情况检测设置才能获取更准确的结果。

筛选 series_decompose_anomalies() 查询的结果以显示 AzureDiagnostics 数据类型中的异常:

显示异常情况检测查询结果的表,已筛选出 Azure 诊断数据类型的结果。

结果显示 6 月 14 日和 6 月 15 日有两个异常。 将这些结果与第一个 make-series 查询的图表进行比较,在其中可以看到 5 月 27 日和 28 日的其他异常:

显示 Azure 诊断表引入的数据总量图表的屏幕截图,其中突出显示了异常。

之所以结果存在差异,是因为 series_decompose_anomalies() 函数相对于预期使用量值对异常进行评分,而该函数根据输入时序中的整个值范围进行计算。

若要从该函数获取更细化的结果,请在该函数的学习过程中排除 6 月 15 日的使用量 – 与时序中的其他值相比,这是一个离群值。

series_decompose_anomalies() 函数的语法为:

series_decompose_anomalies (Series[Threshold,Seasonality,Trend,Test_points,AD_method,Seasonality_threshold])

Test_points 指定要从学习(回归)过程中排除的时序末尾处的点的数量。

若要排除最后一个数据点,请将 Test_points 设置为 1

单击以运行查询

let starttime = 21d; // Start date for the time series, counting back from the current date
let endtime = 0d; // End date for the time series, counting back from the current date
let timeframe = 1d; // How often to sample data
Usage // The table we’re analyzing
| where TimeGenerated between (startofday(ago(starttime))..startofday(ago(endtime))) // Time range for the query, beginning at 12:00 AM of the first day and ending at 12:00 AM of the last day in the time range
| where IsBillable == "true" // Includes only billable data in the result set
| make-series ActualUsage=sum(Quantity) default = 0 on TimeGenerated from startofday(ago(starttime)) to startofday(ago(endtime)) step timeframe by DataType // Creates the time series, listed by data type
| extend(Anomalies, AnomalyScore, ExpectedUsage) = series_decompose_anomalies(ActualUsage,1.5,-1,'avg',1) // Scores and extracts anomalies based on the output of make-series, excluding the last value in the series - the Threshold, Seasonality, and Trend input values are the default values for the function 
| mv-expand ActualUsage to typeof(double), TimeGenerated to typeof(datetime), Anomalies to typeof(double),AnomalyScore to typeof(double), ExpectedUsage to typeof(long) // Expands the array created by series_decompose_anomalies()
| where Anomalies != 0  // Returns all positive and negative deviations from expected usage
| project TimeGenerated,ActualUsage,ExpectedUsage,AnomalyScore,Anomalies,DataType // Defines which columns to return 
| sort by abs(AnomalyScore) desc // Sorts results by anomaly score in descending ordering

筛选 AzureDiagnostics 数据类型的结果:

显示修改后的异常情况检测查询的结果的表,已筛选出 Azure 诊断数据类型的结果。结果现在显示的异常与教程开头部分创建的图表相同。

第一个 make-series 查询的图表中的所有异常现在都出现在结果集中。

分析异常的根本原因

将预期值与离群值进行比较有助于了解两个集之间存在差异的原因。

KQL diffpatterns() 插件比较结构相同的两个数据集,并找出特征化这两个数据集之间的差异的模式。

此查询将 6 月 15 日的 AzureDiagnostics 使用量(示例中的极端离群值)与其他日期的表使用量进行比较:

单击以运行查询

let starttime = 21d; // Start date for the time series, counting back from the current date
let endtime = 0d; // End date for the time series, counting back from the current date
let anomalyDate = datetime_add('day',-1, make_datetime(startofday(ago(endtime)))); // Start of day of the anomaly date, which is the last full day in the time range in our example (you can replace this with a specific hard-coded anomaly date)
AzureDiagnostics	
| extend AnomalyDate = iff(startofday(TimeGenerated) == anomalyDate, "AnomalyDate", "OtherDates") // Adds calculated column called AnomalyDate, which splits the result set into two data sets – AnomalyDate and OtherDates
| where TimeGenerated between (startofday(ago(starttime))..startofday(ago(endtime))) // Defines the time range for the query
| project AnomalyDate, Resource // Defines which columns to return
| evaluate diffpatterns(AnomalyDate, "OtherDates", "AnomalyDate") // Compares usage on the anomaly date with the regular usage pattern

该查询将表中的每个条目标识为发生在 AnomalyDate(6 月 15 日)或 OtherDates。 然后,diffpatterns() 插件拆分这些数据集 - 分别名为 A(示例中的 OtherDates)和 B(示例中的 AnomalyDate)- 并返回导致两个集出现差异的几种模式:

显示包含三行的表的屏幕截图。每行显示异常使用量和基线使用量之间的差异。

查看查询结果可以看到以下差异:

  • 在查询时间范围内的所有其他日期,有 24,892,147 个实例从 CH1-GEARAMAAKS 资源引入了数据,在 6 月 15 日未从该资源引入数据。 从 CH1-GEARAMAAKS 资源引入的数据占查询时间范围内其他日期的总引入量的 73.36%,占 6 月 15 日总引入量的 0%。
  • 在查询时间范围内的所有其他日期,有 2,168,448 个实例从 NSG-TESTSQLMI519 资源引入了数据;在 6 月 15 日,有 110,544 个实例从该资源引入了数据。 从 NSG-TESTSQLMI519 资源引入的数据占查询时间范围内其他日期的总引入量的 6.39%,占 6 月 15 日总引入量的 25.61%。

请注意,在构成其他日期时段的 20 天内,平均有 108,422 个实例(2,168,448 除以 20)从 NSG-TESTSQLMI519 资源引入数据。 因此,6 月 15 日从 NSG-TESTSQLMI519 资源引入的数据量与其他日期从该资源引入的数据量没有显著差异。 但是,由于 6 月 15 日未从 CH1-GEARAMAAKS 引入数据,因此与其他日期相比,从 NSG-TESTSQLMI519 引入的数据量占异常日期总引入量的百分比要大得多。

PercentDiffAB 列显示 A 和 B 之间的百分比差异绝对值 (|PercentA - PercentB|),即两个集之间的差异的主要度量值。 默认情况下,diffpatterns() 插件返回两个数据集之间超过 5% 的差异,但你可以微调此阈值。 例如,若要仅返回两个数据集之间 20% 或更大的差异,可以在上面的查询中设置 | evaluate diffpatterns(AnomalyDate, "OtherDates", "AnomalyDate", "~", 0.20)。 该查询现在只返回一个结果:

显示一个表的屏幕截图,其中的一行显示异常使用量和基线使用量之间的差异。这一次,该查询未返回两个数据集之间小于 20% 的差异。

注意

有关 diffpatterns() 语法和用法的详细信息,请参阅差异模式插件

后续步骤

了解有关以下方面的详细信息: