适用于 Kusto 查询语言查询的最佳做法
下面是提高查询运行速度的几个最佳做法。
摘要
操作 | 用途 | 不要使用 | 注释 |
---|---|---|---|
减少查询的数据量 | 使用 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 运算符 | 当 < 运算符的 > 具有高基数时,请使用 hint.shufflekey=group by keys keysummarize 。 |
理想情况下,高基数高于 100 万。 | |
join 运算符 | 选择行数最少的表作为第一个(查询中最左侧)。 | ||
使用 in 而不是 left semi join 按单个列进行筛选。 |
|||
跨群集联接 | 在联接的“右”侧(大多数数据位于此处),跨远程环境运行查询运行查询,例如群集或 Eventhouses。 | ||
左侧较小,右侧较大时进行联接 | 使用 hint.strategy=broadcast。 | “小”是指最多 100 兆字节 (MB) 数据。 | |
右侧较小,左侧较大时进行联接 | 使用 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
查询运算符时,无论使用单个where
运算符还是多个连续的where
运算符,放置谓词的顺序都会对查询性能产生重大影响。首先应用完整分片谓词。 这意味着应首先应用那些使用 extent_id() 函数 和 extent_tags() 函数 的谓词。 此外,如果有选择性谓词可将数据缩小到特定分区,则应首先应用它们。
然后应用作用于
datetime
表列的谓词。 Kusto 在此类列上包含一个高效索引,通常可以完全消除整个数据分片,而无需访问这些分片。然后应用作用于
string
和dynamic
列的谓词,尤其是在字词级别应用的谓词。 按选择性对谓词进行排序。 例如,有数百万用户时,搜索用户 ID 是非常有选择性的操作,并且通常涉及索引非常有效的字词搜索。然后应用选择性的且基于数字列的谓词。
最后,对于扫描表列数据的查询(例如,对于诸如
contains
"@!@!"
之类没有字词并且不会从索引中受益的谓词),对谓词进行排序,以便将扫描数据较少的列的谓词排在前面。 这减少了解压缩和扫描大型列的需要。
避免使用冗余限定引用
表和具体化视图等实体按名称引用。
例如,可以将表 T
引用为仅 T
(非限定名称),或者通过使用数据库限定符(例如,当表位于名为 DB
的数据库中时为 database("DB").T
)或使用完全限定的名称(例如 cluster("X.Y.kusto.chinacloudapi.cn").database("DB").T
)进行引用。
最佳做法是避免使用冗余的名称限定,原因如下:
未限定的名称更容易识别(对于人类读者来说)为属于范围内的数据库。
引用作用域内数据库实体至少和引用属于其他数据库的实体一样快,在某些情况下甚至更快。
当这些数据库位于不同的群集中时尤其如此。
避免使用限定名称有助于读取者执行正确的操作。
注意
这并不是说限定名称对性能不利。 事实上,Kusto 在大多数情况下能够识别完全限定的名称何时引用属于范围内数据库的实体,并“短路”查询,使其不被视为跨群集查询。 但是,如果没有必要,我们不建议依赖此功能。