使用 “版本 ”下拉列表切换服务。 了解有关导航的详细信息。
适用于: ✅ Azure 数据资源管理器
物化视图由已物化的部分和新摄取但尚未物化的源记录组成,这些记录称为 增量。 物化性能在很大程度上取决于增量与已物化部分之间的重叠程度。 查询性能还取决于在查询整个视图时合并这些部分。 有关详细信息,请参阅 具体化视图的工作原理。
在监控物化视图并确定性能问题的原因后,请使用以下优化。
提高物化内存上限
默认情况下, $materialized-views 工作负荷组将每个具体化操作的内存峰值限制为每个节点 15 GB,或节点总物理内存的 50%(以较低者为准)。 如果具体化因达到此限制而失败,请在工作负荷组中增加 MaxMemoryPerQueryPerNode:
.alter-merge workload_group ['$materialized-views'] ```
{
"RequestLimitsPolicy": {
"MaxMemoryPerQueryPerNode": {
"Value": 34359738368
}
}
}
```
前面的示例将限制增加到每个节点 32 GB。 提高限制后,监控其他负载。 有关详细信息,请参阅 具体化视图工作负荷组。
Note
MaxMemoryPerQueryPerNode 每个节点上可用的总内存不能超过 50%。 一个指定较高值的命令无法通过验证。
调整缓存策略
当扫描未处于热缓存中的数据时,物化过程可能会变慢。 设置缓存策略,使其涵盖物化过程预计会扫描的时间范围。 有关详细信息,请参阅 缓存策略、 .alter 具体化视图策略缓存和 .alter 表策略缓存。
具体化视图的缓存策略仅适用于具体化部分。 源表提供增量数据,也参与查询,因此还应针对所需时段配置源表的缓存策略。 有关详细信息,请参阅 保留和缓存策略。
使用日期/时间分组键
将 datetime 列用作分组键的物化视图可以减少每个周期扫描的物化数据量。 仅当添加日期时间分组键不会改变聚合语义,且该值对于每个唯一实体都是不可变的时,才添加该键。
Note
无法更改现有物化视图的分组键。 若要应用此优化,请创建新的具体化视图。
例如,如果每个 EventId 的 Timestamp 值始终相同,请更改为:
SourceTable | summarize take_any(*) by EventId
更改为:
SourceTable | summarize take_any(*) by EventId, Timestamp
Tip
datetime 分组键中的迟到数据可能会对物化性能产生负面影响。 例如,如果某个视图按 bin(Timestamp, 1d) 分组,并收到一条其 Timestamp 为六个月前的记录,则物化过程可能需要扫描该物化部分此前六个月的数据。 如果该时间段位于冷缓存中,缓存将错过进一步缓慢的具体化。
如果预计会有延迟到达的记录,请配置物化视图和源表的缓存策略,以覆盖预期中最早的值。 如果预计不应出现这些记录,请在物化视图查询中过滤掉它们,或者将其时间戳值规范为当前时间。
添加不可变的创建时间分组键
如果大多数更新都发生在最近创建的实体上,请将不可变的创建时间列添加到分组键中。 物化过程使用第一个日期时间分组键,计算该键在增量部分中的最小值,并将该值用作与已物化部分联接时的下边界。
此更改需要创建一个新的物化视图,因为你无法更改现有视图的分组键。
此优化不是 lookback 属性。 分组键会改变聚合粒度,而配置不当的 lookback 可能会产生重复记录。
此优化仅使用第一个日期时间分组键,因此日期时间分组键的顺序决定了哪一个生效。 如果所选列在增量数据中包含 null 值,则在整个物化周期内都会静默跳过该优化。 当创建时间不包含在每次更新中或添加时更改聚合语义时,请勿使用此优化。
具有较早创建时间值的延迟到达记录,还可能降低或完全抵消某个周期的收益。 单个旧值将联接的下边界向后移动,这可能导致循环扫描具体化部件的较大部分,而不会产生错误。
例如,以可在长达两年内更改的票务预订为例,尽管大多数更改都发生在预订后的两个月内。 如果每个更新都包含不可变 Booking_CreationTimestamp,请更改:
summarize arg_max(Timestamp, *) by BookingId
更改为:
summarize arg_max(Timestamp, *) by BookingId, Booking_CreationTimestamp
然后,实体化过程可以使用增量中最早的 Booking_CreationTimestamp 值作为连接的下限,而不是扫描整个两年时间范围。
定义回溯期
该 lookback 属性限制每个具体化周期对已具体化部分的扫描范围。 它提升的是物化性能,而不是查询性能。
设置足够长的回溯时间,以包含所有预期的重复项或更新。 回溯周期过短可能会产生重复记录。 有关配置详细信息和限制,请参阅 回溯期。
将经常被筛选的列添加为分组键
当查询按物化视图的 GROUP BY 键进行筛选时,查询会得到优化。 如果查询经常按每个唯一实体不可变的列进行筛选,请在分组键中包含该列。
无法更改现有物化视图的分组键。 若要应用此优化,请创建新的具体化视图。
例如,如果某个 ResourceId 始终属于同一个 SubscriptionId,则将物化视图定义为:
.create materialized-view ArgMaxResourceId on table FactResources
{
FactResources
| summarize arg_max(Timestamp, *) by SubscriptionId, ResourceId
}
当查询通常按 SubscriptionId 进行筛选时,相比仅按 ResourceId 分组,这种定义更合适。
将非聚合工作从视图中移出
如果查询只需要对维度表进行查找,请使用该 dimensionTables 属性。 有关详细信息,请参阅 Query 参数。
对于其他转换和规范化,请使用 更新策略 来准备目标表中的数据,只保留具体化视图中的聚合。 例如,定义更新策略:
.alter-merge table Target policy update
@'[{"IsEnabled":true,"Source":"SourceTable","Query":"SourceTable | extend NormalizedResourceId = toupper(ResourceId)","IsTransactional":false,"PropagateIngestionProperties":false}]'
然后,在预处理后的表上定义物化视图:
.create materialized-view Usage on table Target
{
Target
| summarize count() by NormalizedResourceId
}
直接在具体化视图查询中包括转换要求它在每个具体化周期内运行,并且性能可能更糟。
应用分区策略
当大多数查询按物化视图的其中一个分组键进行筛选时,请考虑使用分区策略。 这种优化在多租户数据中很常见,即通过分组键来标识租户。
分区保留单个具体化视图,并可以避免将数据拆分到多个视图。 但是,这会增加盘区的数量,并给物化过程带来更多工作量。
增加可用资源
如果群集没有足够的资源来维持物化视图的正常状态,请增加最小实例数。
优化的自动缩放在做出缩放决策时不会考虑物化视图的健康状况。 如果MaterializedViewResult指标显示InsufficientCapacity,请对集群进行横向扩展(首选),或调整数据引入容量策略。
当多个具体化视图需要同时运行时,请确保具体化视图容量策略允许足够的并发性。 仅在评估对其他工作负荷的影响后增加 ClusterMinimumConcurrentOperations 。 有关详细信息,请参阅 具体化视图容量策略。
例如,以下命令将并发具体化操作的最小数目设置为 3:
.alter-merge cluster policy capacity '{ "MaterializedViewsCapacity": { "ClusterMinimumConcurrentOperations": 3 } }'
拆分为多个具体化视图
当单个物化视图的物化周期需要占用过多内存,而前述优化措施又不足以解决问题时,进行拆分会有所帮助。 拆分必须减少分组数量以及每个物化视图处理的状态量。 不要将 KQL 分成多个处理阶段。
虽然拆分可能会增加 CPU 使用率,但它减少了具体化周期中的内存峰值。 确保 ClusterMinimumConcurrentOperations 允许拆分视图并发运行。 否则,这些视图可能会串行运行,从而无法获得拆分带来的好处。
对于任一拆分方法,请确保:
- 分区表达式具有确定性,并且在这些视图的整个生命周期内保持不变。
- 分区键是分组键的一部分。
- 分布合理均衡。
避免随机拆分,也不要依据对于同一逻辑组可能会变化的值进行拆分。
按稳定的键进行水平分片
假设原始具体化视图为:
.create materialized-view UsageMV on table Events
{
Events
| summarize EventCount = count(), TotalBytes = sum(Bytes)
by TenantId, Day = bin(Timestamp, 1d)
}
使用 hash_xxhash64() 将租户确定性地分配到四个物化视图中:
.create materialized-view UsageMV_0 on table Events
{
Events
| where hash_xxhash64(TenantId, 4) == 0
| summarize EventCount = count(), TotalBytes = sum(Bytes)
by TenantId, Day = bin(Timestamp, 1d)
}
为存储桶 1、2 和 3 创建等效的具体化视图,并通过函数公开它们:
.create-or-alter function Usage()
{
union UsageMV_0, UsageMV_1, UsageMV_2, UsageMV_3
}
由于每个组完全属于一个具体化视图,因此不需要最终重新聚合。 哈希函数和桶数在这些视图的整个生命周期内必须保持固定。 更改其中任一项都可能会将现有组分配到另一个视图,因此您必须重新创建并回填所有分片视图。
按自然业务分区拆分
此方法通常比哈希更清晰:
.create materialized-view Usage_EU on table Events
{
Events
| where Region == "EU"
| summarize count(), sum(Bytes)
by Region, TenantId, Day = bin(Timestamp, 1d)
}
为其他区域创建相应的视图。 当查询通常按分区键进行筛选时,此方法最有效。
优化查询
如果可以容忍某些数据延迟,请使用 materialized_view() 函数 仅查询具体化部分。 此方法可避免在查询时将具体化部分与增量组合在一起。
查询整个视图时,请尽可能按分组键进行筛选。 还可以测试 materialized_view_shuffle 客户端请求属性,以控制 summarize 和 join 操作的 shuffle 策略。 指定键以获取类似于 hint.shufflekey的行为,或省略键以获取类似于 hint.strategy=shuffle的行为。 有关详细信息和示例,请参阅 具体化视图查询优化器。