Azure监视器日志是一项完全托管的云规模服务,旨在自动处理大型和波动工作负荷的引入、索引和查询。 其基础引擎采用内置机制来优化查询执行、分发处理,并自动无缝缩放资源,而无需用户干预。
Azure监视器日志使用 Azure Data Explorer 作为其基础引擎的一部分来存储日志数据并运行查询来分析这些数据。 它为你创建、管理和维护Azure Data Explorer群集,并针对日志分析工作负荷对其进行优化。 运行查询时,服务会对其进行优化,并将其路由到存储工作区数据的相应Azure Data Explorer群集。
Azure Monitor 日志和 Azure 数据资源管理器使用许多自动查询优化机制。 与任何大型分析系统一样,跨非常大的数据集运行查询需要额外的计算资源,并可能会影响查询性能。 尽管自动优化提供了显著的提升,但在某些情况下,可以显著提高查询性能。 本文介绍了性能注意事项和解决相关问题的几种方法。
大多数方法对于直接在 Azure 数据资源管理器和 Azure Monitor 日志上运行的查询是通用的。 我们也会探讨一些独特的 Azure Monitor 日志注意事项。 如需更多的 Azure 数据资源管理器优化技巧,请参阅查询最佳做法。
优化的查询:
- 运行速度更快,并减少查询执行的总体持续时间。
- 不太可能被限速或拒绝。
请特别注意反复使用的和同时使用的查询,例如涉及仪表板、警报、Azure 逻辑应用和 Power BI 的查询。 在这些情况下,无效查询的影响是巨大的。
查询详细信息窗格
在 Log Analytics 中运行查询后,选择屏幕右下角的“查询详细信息”,打开“查询详细信息”窗格。 “查询详细信息”窗格包含三个选项卡:
- 概述 - 一组特选的关键绩效指标
- 原始统计信息 - 所有详细的执行统计信息、引用的工作区和查询以及其他技术数据
- 错误 - 列出查询执行期间遇到的任何错误
此 “概述 ”选项卡显示查询的多个性能指标的结果。 下一部分介绍了这些性能指标。
查询执行时间细分
在 Azure Monitor 日志中分析查询性能时,“查询详细信息”窗格提供一个 执行时间 ,反映从端到端查询的总体持续时间。 此统计信息分为三个不同的组件,以帮助识别性能瓶颈:
引擎执行时间 这是在基础数据引擎(例如 Azure 数据资源管理器或其他组件)中执行查询所用的时间。 如果此值是总体执行时间的主要参与者,则可能表示可以优化查询本身。 请参阅本文档中所述的优化技术以提高性能。
服务执行时间 这表示在数据引擎外部的 Azure Monitor 日志服务本身中花费的时间。 它包括内部处理和编排。
服务队列时间 这是执行之前查询在 Azure Monitor 日志服务队列中等待的时间。 如果填充了此值(例如,而不是 N/A 的默认值),则建议用户由于多个同时查询而达到并发限制。 高队列时间表示其他并发查询可能占用大量资源,应进行评审和优化,以减少争用。 请参阅 Azure Monitor 服务限制 - Azure Monitor |Azure Learn。 Azure Monitor 服务限制 - 用户查询限制
查询性能指标
运行的每个查询都提供以下查询性能指标:
- 总 CPU:用于处理查询的总体计算量,通过所有计算节点。 它表示用于计算、分析和数据提取的时间。
- 已处理的查询的时间跨度:查询访问的最新数据和最早数据之间的差距。 为查询指定的显式时间范围会影响此指示器。
- 已处理数据的年龄:现在与查询访问的最早数据之间的差距。 此指示器严重影响数据提取的效率。
- 工作区数:根据隐式或显式选择,查询在处理期间访问的工作区数。
- 区域数:根据工作区的隐式或显式选择,查询在查询处理期间访问的区域数。 多区域查询的效率要低得多,并且性能指标只覆盖了部分区域。
- 并行度:系统对多个节点上执行查询的量。 此指示器仅与 CPU 使用率较高的查询相关。 特定函数和运算符的使用会影响此指示器。
- 内存峰值:执行此查询时系统使用的最大内存量。 此值包括执行过程中数据加载、处理和临时storage消耗的内存。
总 CPU
此指标显示在所有查询处理节点中处理查询时实际使用的计算 CPU。 由于大多数查询在大量节点上运行,因此此总数通常比执行查询所花费的持续时间要大得多。
如果查询使用超过 100 秒的 CPU,则会消耗过多的资源。 如果查询消耗 CPU 超过 1,000 秒,则它被视为滥用查询,可能会受到限制处理。
查询处理的时间用于:
- 数据检索:检索旧数据比检索最近数据所需的时间更多。
- 数据处理:数据的逻辑和计算。
除了查询处理节点花费的时间外,Azure Monitor 日志还会在以下方面花费时间:
- 对用户进行身份验证并验证他们是否有权访问此数据。
- 查找数据存储。
- 解析查询。
- 分配查询处理节点。
此时间不包括在查询总 CPU 时间内。
使用高 CPU 函数之前提前筛选记录
某些查询命令和函数使用高 CPU。 对于分析 JSON 和 XML 或提取复杂的正则表达式的命令尤其如此。 此类分析可以通过 parse_json() 或 parse_xml() 函数显式发生,或在引用动态列时隐式发生。
这些函数使用的 CPU 与它们正在处理的行数成正比。 最有效的优化是提前在查询中添加 where 条件。 通过添加这些条件,可以在 CPU 密集型函数运行之前筛选出尽可能多的记录。
例如以下查询会生成完全相同的结果。 但第二个查询的效率最高,因为其中的 where 条件在分析之前排除了许多记录:
//less efficient
SecurityEvent
| extend Details = parse_xml(EventData)
| extend FilePath = tostring(Details.UserData.RuleAndFileData.FilePath)
| extend FileHash = tostring(Details.UserData.RuleAndFileData.FileHash)
| where FileHash != "" and FilePath !startswith "%SYSTEM32" // Problem: irrelevant results are filtered after all processing and parsing is done
| summarize count() by FileHash, FilePath
//more efficient
SecurityEvent
| where EventID == 8002 //Only this event have FileHash
| where EventData !has "%SYSTEM32" //Early removal of unwanted records
| extend Details = parse_xml(EventData)
| extend FilePath = tostring(Details.UserData.RuleAndFileData.FilePath)
| extend FileHash = tostring(Details.UserData.RuleAndFileData.FileHash)
| where FileHash != "" and FilePath !startswith "%SYSTEM32" // exact removal of results. Early filter is not accurate enough
| summarize count() by FileHash, FilePath
| where FileHash != "" // No need to filter out %SYSTEM32 here as it was removed before
避免使用涉及计算的 where 子句
如果查询包含的 where 子句基于某个计得列而不是数据集中实际存在的列,则查询的效率会降低。 在处理大型数据集时,针对计得列进行筛选会妨碍某些系统优化。
例如以下查询会生成完全相同的结果。 但第二个方法效率更高,因为 where 条件引用了内置列:
//less efficient
Syslog
| extend Msg = strcat("Syslog: ",SyslogMessage)
| where Msg has "Error"
| count
//more efficient
Syslog
| where SyslogMessage has "Error"
| count
在某些情况下,查询处理引擎会隐式地创建评估列,因为筛选不仅仅是基于字段。
//less efficient
SecurityEvent
| where tolower(Process) == "conhost.exe"
| count
//more efficient
SecurityEvent
| where Process =~ "conhost.exe"
| count
在汇总和联接中使用高效的聚合命令和维度
某些聚合命令(如 max()、sum()、count() 和 avg())的 CPU 影响较低。 其他命令则较复杂,包括试探方法和估算,这使它们能够高效地执行。 例如,dcount() 使用 HyperLogLog 算法来提供对大型数据集中不同元素计数的近似估计,而无需逐一计算每个值。
百分位函数使用最近的秩百分位算法执行类似的粗略估算。 多个命令包括了可选参数来降低其影响。 例如,makeset() 函数具有一个可选参数来定义最大集大小,这严重影响了 CPU 和内存。
Join 和 summarize 命令在处理大量数据时可能会导致 CPU 使用率较高。 它们的复杂性与用作 summarize 中 by 或 join 属性的列的可能值的数量直接相关,这些可能值被称为“基数”。 有关对 join 和 summarize 的说明和优化,请参阅它们的文档文章和优化技巧。
例如,下面的查询产生完全相同的结果,因为 CounterPath 始终一对一映射到 CounterName 和 ObjectName。 第二个查询效率更高,因为聚合维度较小:
//less efficient
Perf
| summarize avg(CounterValue)
by CounterName, CounterPath, ObjectName
//make the group expression more compact improve the performance
Perf
| summarize avg(CounterValue), any(CounterName), any(ObjectName)
by CounterPath
CPU 消耗还可能会受 where 条件或需要密集计算的扩展列的影响。 所有简单字符串比较(如 equal == 和 startswith)的 CPU 影响大致相同。 而高级文本匹配则影响更大。 具体而言,has 运算符比 contains 运算符更高效。 由于使用的字符串处理技术,查找长度超过四个字符的字符串比查找短字符串更高效。
例如,下面的查询将产生类似的结果,具体取决于 Computer 命名策略。 但第二个更高效。
//less efficient - due to filter based on contains
Heartbeat
| where Computer contains "Production"
| summarize count() by ComputerIP
//less efficient - due to filter based on extend
Heartbeat
| extend MyComputer = Computer
| where MyComputer startswith "Production"
| summarize count() by ComputerIP
//more efficient
Heartbeat
| where Computer startswith "Production"
| summarize count() by ComputerIP
注意
此指标仅显示来自紧邻群集的 CPU。 在多区域查询中,它仅表示其中一个区域。 在多工作区查询中,它可能不包括所有工作区。
当字符串分析有效时,请避免使用完全 XML 和 JSON 分析
完全分析某个 XML 或 JSON 对象可能会消耗大量 CPU 和内存资源。 在许多情况下,当只需要一个或两个参数并且 XML 或 JSON 对象很简单时,可以更轻松地将它们解析为字符串。 使用 parse 运算符或其他 文本分析技术。 随着 XML 或 JSON 对象中的记录数的增加,性能提升更为显著。 当记录的数量达到数千万时,这一点至关重要。
例如,下面的查询返回与上文的查询完全相同的结果,而无需执行全部 XML 分析。 查询对 XML 文件结构做了一些假设,例如 FilePath 元素位于 FileHash 之后,并且它们都没有属性:
//even more efficient
SecurityEvent
| where EventID == 8002 //Only this event have FileHash
| where EventData !has "%SYSTEM32" //Early removal of unwanted records
| parse EventData with * "<FilePath>" FilePath "</FilePath>" * "<FileHash>" FileHash "</FileHash>" *
| summarize count() by FileHash, FilePath
| where FileHash != "" // No need to filter out %SYSTEM32 here as it was removed before
分解大型分析命令
使用 parse 运算符时,将单个语句中提取的列数限制为五列。 单个语句中的提取次数过多可能会导致处理时间显著增加。 相反,应将提取拆分为多个 parse 语句。
注意
在转换中,parse 运算符在单个语句中限制为 10 个提取。
例如,以下查询会产生相同的结果,但第二个查询的效率要高得多,因为它将分析操作分解为多个较小的命令。
//less efficient
LogData
| parse Message with
* "field1=" Field1: string
" field2=" Field2: string
" field3=" Field3: string
" field4=" Field4: string
" field5=" Field5: string
" field6=" Field6: string
" field7=" Field7: string
" field8=" Field8: string
" field9=" Field9: string
" field10=" Field10: string *
//more efficient
LogData
| parse Message with
* "field1=" Field1: string
" field2=" Field2: string
" field3=" Field3: string
" field4=" Field4: string
" field5=" Field5: string *
| parse Message with
* " field6=" Field6: string
" field7=" Field7: string
" field8=" Field8: string
" field9=" Field9: string
" field10=" Field10: string *
避免不必要地使用 search 和 union 运算符
影响数据处理量的一个因素是使用了大量的表格。 这种情况通常在使用 search * 和 union * 命令时发生。 这些命令强制系统对工作区中所有表的数据进行评估和扫描。 在某些情况下,工作区中可能会有数百张表格。 请避免使用 search * 或未将范围限定为特定表的任何搜索。
例如,下面的查询生成完全相同的结果,但最后一个查询最高效:
// This version scans all tables though only Perf has this kind of data
search "Processor Time"
| summarize count(), avg(CounterValue) by Computer
// This version scans all strings in Perf tables - much more efficient
Perf
| search "Processor Time"
| summarize count(), avg(CounterValue) by Computer
// This is the most efficient version
Perf
| where CounterName == "% Processor Time"
| summarize count(), avg(CounterValue) by Computer
向查询添加早期筛选器
可以通过在查询早期添加 where 条件来减少数据量。 Azure 数据资源管理器平台包含一个缓存,该缓存可让它知道哪些分区包含与特定 where 条件相关的数据。 例如,如果查询包含 where EventID == 4624,则它仅将查询分发给处理具有匹配事件的分区的节点。
下面的示例查询生成完全相同的结果,但第二个查询更高效:
//less efficient
SecurityEvent
| summarize LoginSessions = dcount(LogonGuid) by Account
//more efficient
SecurityEvent
| where EventID == 4624 //Logon GUID is relevant only for logon event
| summarize LoginSessions = dcount(LogonGuid) by Account
避免对同一源数据的多次扫描
当查询有多个子查询由联接或联合运算符合并时,每个子查询单独扫描整个源。 然后,查询会将结果合并。 此操作将增加数据扫描的次数,这是影响大型数据集查询性能的关键因素。
若要避免出现此性能问题,请使用条件聚合函数。 在摘要运算符中使用的大多数 聚合函数都有条件版本。 使用带有多个条件的条件版本来获取单个摘要运算符。
例如,以下查询显示每个帐户的登录事件数和进程执行事件数。 它们返回相同的结果,但第一个查询会扫描数据两次。 第二个查询仅扫描它一次:
//Scans the SecurityEvent table twice and perform expensive join
SecurityEvent
| where EventID == 4624 //Login event
| summarize LoginCount = count() by Account
| join
(
SecurityEvent
| where EventID == 4688 //Process execution event
| summarize ExecutionCount = count(), ExecutedProcesses = make_set(Process) by Account
) on Account
//Scan only once with no join
SecurityEvent
| where EventID == 4624 or EventID == 4688 //early filter
| summarize LoginCount = countif(EventID == 4624), ExecutionCount = countif(EventID == 4688), ExecutedProcesses = make_set_if(Process,EventID == 4688) by Account
不需要子查询的另一种情况是预筛选 parse 运算符以确保它只处理与特定模式匹配的记录。 这样做是不必要的,因为 parse 运算符和其他类似的运算符在模式不匹配时会返回空结果。 以下两个查询返回的结果完全相同,但第二个查询仅扫描一次数据。 在第二个查询中,每个 parse 命令只与其事件相关。
extend 运算符会在之后显示如何引用空数据情况:
//Scan SecurityEvent table twice
union(
SecurityEvent
| where EventID == 8002
| parse EventData with * "<FilePath>" FilePath "</FilePath>" * "<FileHash>" FileHash "</FileHash>" *
| distinct FilePath
),(
SecurityEvent
| where EventID == 4799
| parse EventData with * "CallerProcessName\">" CallerProcessName1 "</Data>" *
| distinct CallerProcessName1
)
//Single scan of the SecurityEvent table
SecurityEvent
| where EventID == 8002 or EventID == 4799
| parse EventData with * "<FilePath>" FilePath "</FilePath>" * "<FileHash>" FileHash "</FileHash>" * //Relevant only for event 8002
| parse EventData with * "CallerProcessName\">" CallerProcessName1 "</Data>" * //Relevant only for event 4799
| extend FilePath = iif(isempty(CallerProcessName1),FilePath,"")
| distinct FilePath, CallerProcessName1
如果上述查询不允许避免使用子查询,另一种方法是向查询引擎提示,其中每个查询都有一个数据源,使用 materialize() 函数。 当源数据来自查询中多次使用的函数时,此方法非常有用。 当子查询的输出比输入小得多时,Materialize 是有效的。 查询引擎会缓存并在所有场合中重复使用该输出。
减少检索的列数
因为 Azure 数据资源管理器是一个列式数据存储,所以对每一列的检索都独立于其他列。 检索的列数直接影响到整个数据量。 仅在输出中包含您需要的列,可以通过汇总结果或投影特定列。
Azure 数据资源管理器采取了多项优化来减少检索的列的数量。 如果它确定不需要列,例如,如果未在 summarize 命令中引用列,则它不会检索它。
例如,第二个查询可能会处理的数据量是原来的三倍,因为它需要获取的不是一列而是三列。
//Less columns --> Less data
SecurityEvent
| summarize count() by Computer
//More columns --> More data
SecurityEvent
| summarize count(), dcount(EventID), avg(Level) by Computer
已处理查询的时间跨度
Azure Monitor 日志中的所有日志都根据 TimeGenerated 列进行分区。 查询访问的分区数与时间跨度直接相关。 减小时间范围是确保快速执行查询的最有效方法。
时间跨度超过 15 天的查询被视为消耗过多资源的查询。 时间跨度超过 90 天的查询被视为滥用查询,可能会受到限制。
可以使用 Log Analytics 屏幕中的时间范围选择器设置时间范围,如 Azure Monitor Log Analytics 中的日志查询范围和时间范围中所述。 这是推荐使用的方法,因为选定的时间范围将通过使用查询元数据传递到后端。
另一种方法是在查询中显式包含关于where的TimeGenerated条件。 你应当使用此方法,因为它可以确保时间跨度是固定的,即使查询是从不同的接口使用的。
确保查询的所有部分都有 TimeGenerated 筛选器。 当查询包含从各种表或同一个表提取数据的子查询时,每个查询必须包含其自己的where条件。
确保所有子查询都具有 TimeGenerated 筛选器
例如,在以下查询中,Perf 表仅扫描最后一天的数据。 扫描 Heartbeat 该表的所有历史记录,最长可能为两年:
Perf
| where TimeGenerated > ago(1d)
| summarize avg(CounterValue) by Computer, CounterName
| join kind=leftouter (
Heartbeat
//No time span filter in this part of the query
| summarize IPs = makeset(ComputerIP, 10) by Computer
) on Computer
发生此类错误的常见情况是,arg_max()用于查找最近的事件。 例如:
Perf
| where TimeGenerated > ago(1d)
| summarize avg(CounterValue) by Computer, CounterName
| join kind=leftouter (
Heartbeat
//No time span filter in this part of the query
| summarize arg_max(TimeGenerated, *), min(TimeGenerated)
by Computer
) on Computer
通过在内部查询中添加时间筛选器,可以轻松地纠正此问题:
Perf
| where TimeGenerated > ago(1d)
| summarize avg(CounterValue) by Computer, CounterName
| join kind=leftouter (
Heartbeat
| where TimeGenerated > ago(1d) //filter for this part
| summarize arg_max(TimeGenerated, *), min(TimeGenerated)
by Computer
) on Computer
另一个错误示例是在多个表上执行union后立即进行时间范围筛选。 在执行 union 运算时,应确定每个子查询的范围。 可以使用 [let]'(https://learn.microsoft.com/azure/kusto/query/letstatement) 语句来确保范围一致性。
例如,以下查询扫描Heartbeat和Perf表中的所有数据,而不仅仅扫描最后一天的数据。
Heartbeat
| summarize arg_min(TimeGenerated,*) by Computer
| union (
Perf
| summarize arg_min(TimeGenerated,*) by Computer)
| where TimeGenerated > ago(1d)
| summarize min(TimeGenerated) by Computer
要修复查询,请执行以下步骤:
let MinTime = ago(1d);
Heartbeat
| where TimeGenerated > MinTime
| summarize arg_min(TimeGenerated,*) by Computer
| union (
Perf
| where TimeGenerated > MinTime
| summarize arg_min(TimeGenerated,*) by Computer)
| summarize min(TimeGenerated) by Computer
时间跨度度量限制
度量值始终大于指定的实际时间。 例如,如果查询上的筛选器为 7 天,则系统可能会扫描 7.5 或 8.1 天。 此方差是因为系统将数据分区为可变大小的区块。 为了确保查询扫描所有相关记录,系统将扫描整个分区。 此过程可能需要几个小时,甚至超过一天。
几种情况阻止系统提供时间范围的准确度量。 在大多数情况下,如果查询范围少于一天或者是查询为多工作区查询,就会出现这种现象。
重要
此指标仅显示在紧邻群集中处理的数据。 在多区域查询中,它仅表示其中一个区域。 在多工作区查询中,它可能不包括所有工作区。
已处理数据的年限
Azure 数据资源管理器使用多个存储层:内存中、本地 SSD 磁盘,以及速度慢得多的 Azure Blob。 数据越新,越有可能存储在性能更高且延迟更小的层中,这能减少查询持续时间和 CPU 占用。 除了数据本身以外,系统还有元数据的缓存。 数据越旧,其元数据在缓存中的可能性就越小。
处理超过 14 天的数据的查询被视为消耗过多资源的查询。
某些查询需要使用旧数据,但某些查询错误地使用旧数据。 执行查询时,如果没有在其元数据中提供时间范围并且不是所有表引用都包含针对 TimeGenerated 列的筛选器,则会发生这种情况。 在这些情况下,系统会扫描表中存储的所有数据。 当数据保留时间较长时,它可能会涵盖较长的时间范围。 因此会扫描在存在时间上与数据保留期一样长的数据。
此类情况的示例包括以下情况:
- 在 Log Analytics 中未设置无限制子查询的时间范围。 请参阅前面的示例。
- 在不使用时间范围可选参数的情况下使用 API。
- 使用不强制时间范围的客户端,例如 Power BI 连接器。
请参阅上一部分中的示例和注释,因为它们在本例中也相关。
区域数量
在某些情况下,可以在不同区域执行单个查询。 例如:
- 显式列出多个工作区,它们位于不同的区域。
- 资源范围的查询提取数据,数据存储在位于不同区域的多个工作区中。
跨区域执行查询要求系统在后端序列化和传输通常比查询最终结果大得多的大块中间数据。 它还限制了系统执行优化和试探法以及使用缓存的能力。
如果不需要扫描所有这些区域,请调整范围,使其涵盖较少的区域。 如果最小化资源范围,但仍使用许多区域,则配置错误可能是原因。 例如,审核日志和诊断设置发送到不同区域中的不同工作区,或者存在多个诊断设置配置。
跨三个以上的区域的查询被视为消耗过多资源的查询。 跨 6 个以上的区域的查询被视为滥用查询,可能会受到限制。
重要
在多个区域运行查询时,CPU 和数据度量不准确,并且仅表示其中一个区域的度量。
工作区数量
使用工作区作为逻辑容器来隔离和管理日志数据。 后端会优化选定区域内物理群集的工作区位置。
多个工作区来自以下情况:
- 明确列出了多个工作区。
- 资源范围的查询提取存储在多个工作区中的数据。
跨区域和跨群集执行查询要求系统在后端序列化和传输通常比查询最终结果大得多的大块中间数据。 它还限制了系统执行优化和试探法以及使用缓存的能力。
跨 5 个以上的工作区的查询消耗过多的资源。 查询不能跨越 100 个以上的工作区。
重要
- 在某些多工作区方案中,CPU 和数据度量并不准确,并且仅表示对几个工作区的度量。
- 使用显式标识符(例如工作区 ID 或工作区Azure资源 ID)跨工作区查询,消耗的资源更少,性能更好。
有关详细信息,请参阅 跨资源查询。
并行度
Azure Monitor 日志使用 Azure 数据资源管理器的大型群集来运行查询。 这些群集的规模各不相同,最多可以包含数十个计算节点。 系统会根据工作区放置逻辑和容量自动缩放群集。
为了有效地执行查询,系统对查询进行分区,并根据处理所需的数据将其分发到计算节点。 在某些情况下,系统无法有效地执行此步骤,这可能会导致查询持续时间较长。
可能会降低并行度的查询行为包括:
- 在许多情况下,
join和summarize等运算符的总体并行度较低。 考虑在出现性能问题时使用shuffle。 当键(即联接或汇总的列)具有高基数(很多唯一值)时,请使用洗牌查询。 例如,当列包含公共 IP 地址时,可以使用洗牌功能。 避免对具有低基数的键使用混排(例如事件的严重性级别)。 使用多个洗牌查询时显式使用hint.shufflekey,以确保每个键生效。 - 使用序列化和窗口函数,例如
serialize运算符、next()、prev()和row函数。 在这些情况下,有时候可能会使用时序和用户分析功能。 如果在查询结束时未使用以下运算符,则也可能发生低效序列化:range、sort、order、top、top-hitters和getschema。 - 使用
dcount()聚合函数强制系统保存唯一值的中央副本。 当数据规模较大时,请考虑使用dcount函数可选参数来降低精度。 - 在资源范围查询中,在存在大量 Azure 角色分配的情况下,预执行的 Kubernetes 基于角色的访问控制 (RBAC)或 Azure RBAC 检查可能会延迟。 这种情况可能会导致检查时间更长,导致并行度较低。 例如,查询可能在存在数千个资源的订阅上执行,每个资源在资源级别而非订阅或资源组上有许多角色分配。
- 如果查询处理少量数据块,则其并行度较低,因为系统不会将其分散到许多计算节点。
内存峰值
内存峰值是执行查询时 Azure 数据资源管理器引擎观察到的最大 RAM 量。 它涵盖用于数据加载(缓存/热读取)、运算符处理(例如联接、汇总、生成系列)和临时工作集的内存。 它是失控内存状态的一个主要指标,这些状态会触发诸如失控查询(E_RUNAWAY_QUERY,操作员超出内存预算)和E_LOW_MEMORY_CONDITION等保护措施。 监控内存峰值有助于提前捕获这些模式并优化查询,以防止达到相关的硬性限制。
如何减少内存高峰
- 遵循与针对 总 CPU 中所述的相同做法。 具体而言,对于查询中的每个表,应用提前记录筛选和列投影。
- 在许多情况下,join 和
summarize等运算符会导致内存使用率高,并可能导致失控查询。 考虑在出现性能问题时使用shuffle。 当键(换句话说,联接列或汇总列)具有较高的基数时,请使用随机键。 例如,当列包含公共 IP 地址时,请使用洗牌功能。 避免对具有低基数的键使用混排(例如事件的严重性级别)。 - 如果使用
join,请使用适用的最佳做法。 请参阅 查询最佳做法。 - 请考虑使用采样。