适用于 Kusto 查询语言查询的最佳做法

适用于:✅Azure 数据资源管理器Azure MonitorMicrosoft Sentinel

下面是提高查询运行速度的几个最佳做法。

摘要

操作 用途 不要使用 注释
减少查询的数据量 使用 where 运算符等机制减少要处理的数据量。 请参阅下文,了解减少处理的数据量的有效方式。
避免使用冗余限定引用 引用本地实体时,请使用非限定名称。 有关该主题的详细信息,请参阅下文。
datetime 使用 datetime 数据类型。 请勿使用 long 数据类型。 在查询中,请勿使用 unix 时间转换函数,例如 unixtime_milliseconds_todatetime()。 相反,使用更新策略在引入期间将 unix 时间转换为 datetime 数据类型。
字符串运算符 使用 has 运算符 不要使用 contains 查找完整标记时,has 效果更好,因为它不会查找子字符串。
区分大小写的运算符 使用 == 不要使用 =~ 如果可能,请使用区分大小写的运算符。
使用 in 不要使用 in~
使用 contains_cs 不要使用 contains 如果可以使用 has/has_cs 而不使用 contains/contains_cs,则更好。
搜索文本 查找特定列 不要使用 * * 对所有列执行全文搜索。
从数百万行的动态对象中提取字段 如果大多数查询从数百万行的动态对象中提取字段,则会在引入时具体化列。 这样,只需为列提取付费一次。
查找动态对象中不常见的键/值 使用 MyTable | where DynamicColumn has "Rare value" | where DynamicColumn.SomeKey == "Rare value" 不要使用 MyTable | where DynamicColumn.SomeKey == "Rare value" 这样可以筛选出大多数记录,并只对其余部分进行 JSON 分析。
具有多次使用的值的 let 语句 使用 materialize() 函数 有关如何使用 materialize() 的详细信息,请参阅 materialize()。 有关详细信息,请参阅优化使用命名表达式的查询
对超过 10 亿条记录应用转换 调整查询以减少馈送到转换中的数据量。 如果可以避免,请勿转换大量数据。
新查询 在末尾使用 limit [small number]count 对未知数据集运行未绑定的查询,可能会产生要返回到客户端的 GB 级结果,从而导致响应缓慢和群集忙碌。
不区分大小写的比较 使用 Col =~ "lowercasestring" 不要使用 tolower(Col) == "lowercasestring"
比较已小写(或大写)的数据 Col == "lowercasestring"(或 Col == "UPPERCASESTRING" 避免使用不区分大小写的比较。
按列筛选 按表列筛选。 不要按计算列进行筛选。
使用 T | where predicate(*Expression*) 不要使用 T | extend _value = *Expression* | where predicate(_value)
summarize 运算符 当 summarize 运算符的 group by keys 具有高基数时,请使用 hint.shufflekey=<key> 理想情况下,高基数高于 100 万。
join 运算符 选择行数较少的表作为第一个(查询中最左侧)。
使用 in 而不是 left semi join 按单个列进行筛选。
跨群集联接 在群集中,在联接的“右”侧(大多数数据位于此处)运行查询。
左侧较小,右侧较大时进行联接 使用 hint.strategy=broadcast “小”是指最多 100MB 数据。
右侧较小,左侧较大时进行联接 使用 lookup 运算符而不是 join 运算符 如果查找的右侧大于几十 MB,则查询将失败。
当两侧都过大时进行联接 使用 hint.shufflekey=<key> 当联接键具有高基数时使用。
提取具有相同格式或模式的字符串的列上的值 使用 parse 运算符 不要使用几个 extract() 语句。 例如,像 "Time = <time>, ResourceId = <resourceId>, Duration = <duration>, ...." 这样的值
extract() 函数 当分析的字符串不都遵循相同的格式或模式时使用。 使用 REGEX 提取所需的值。
materialize() 函数 推送所有可能的运算符,这些运算符将减少具体化的数据集,并且仍然保留查询的语义。 例如,筛选器或仅项目所需的列。 有关详细信息,请参阅优化使用命名表达式的查询
使用具体化视图 使用具体化视图存储常用聚合。 首选使用 materialized_view() 函数以仅查询具体化部件 materialized_view('MV')

减少要处理的数据量

查询性能直接取决于它需要处理的数据量。 处理的数据越少,查询速度越快(并且消耗的资源越少)。 因此,最重要的最佳做法是以减少要处理的数据量的方式构建查询。

注意

在下面的讨论内容中,请务必记住筛选器选择性的概念。 选择性是指当按某个谓词进行筛选时,记录被筛选出的百分比。 高选择性谓词是指应用该谓词后仅保留少量记录,从而减少需要有效处理的数据量。

按重要性排序:

  • 仅引用查询需要其数据的表。 例如,如果将 union 运算符与通配符表引用结合使用,则从性能角度看,最好仅引用少量的表,而不要使用通配符 (*) 引用所有表,然后使用谓词根据源表名称筛选出数据。

  • 如果查询仅与特定范围相关,请利用表的数据范围。 table() 函数提供了一种有效方式用于根据缓存策略(DataScope 参数)确定数据范围来消除数据。

  • 在表引用之后紧接着应用 where 查询运算符。

  • 使用 where 查询运算符时,明智地使用谓词顺序(在单个运算符中,或在多个连续运算符中,两种方式都可以)可以对查询性能产生重大影响,如下所述。

  • 首先应用完整分片谓词。 这意味着,应首先应用使用 extent_id() 函数的谓词、使用 extent_tags() 函数的谓词,以及对表的数据分区具有很强选择性的谓词(如果已定义)。

  • 然后应用作用于 datetime 表列的谓词。 Kusto 在此类列上包含一个非常高效的索引,通常可以完全消除整个数据分片,而无需访问这些分片。

  • 然后应用作用于 stringdynamic 列的谓词,尤其是在字词级别应用的谓词。 谓词应按选择性排序(例如,有数百万用户时,搜索用户 ID 是非常有选择性的操作,并且通常是索引非常有效的字词搜索。)

  • 然后应用选择性的且基于数字列的谓词。

  • 最后,对于扫描表列数据的查询(例如,对于诸如 `contains "@!@!" 之类没有字词并且不会从索引中受益的谓词),对谓词进行排序,以便将扫描数据较少的列的谓词排在前面。 这减少了解压缩和扫描大型列的需要。

避免使用冗余限定引用

表和具体化视图等实体按名称引用。

例如,可以将表 T 引用为仅 T非限定名称),或者通过使用数据库限定符(例如,当表位于名为 DB 的数据库中时为 database("DB").T)或使用完全限定的名称(例如 cluster("X.Y.kusto.chinacloudapi.cn").database("DB").T)进行引用。

最佳做法是避免在名称限定冗余时使用名称限定,原因如下:

  1. 未限定的名称更容易识别(对于人类读者来说)为属于范围内的数据库。

  2. 引用作用域内数据库实体至少和引用属于其他数据库的实体一样快,在某些情况下甚至更快。

(当这些数据库位于不同的群集中时尤其如此。)

避免使用限定名称有助于读取者执行正确的操作。

注意

这并不是说限定名称对性能不利。 事实上,Kusto 在大多数情况下能够识别完全限定的名称何时引用属于范围内数据库的实体,并“短路”查询,使其不被视为跨群集查询。 但是,出于上述原因,我们建议在不必要的情况下不要依赖此功能。