Query Profile 结构与详细指标
Query Profile 的结构
Query Profile 的结构与执行引擎的设计密切相关,由以下五部分组成:
- Fragment:执行树。一个查询由一个或多个 Fragment 组成。
- FragmentInstance:每个 Fragment 可以有多个实例,每个实例称为 FragmentInstance,分别由不同的计算节点来执行。
- Pipeline:一个 FragmentInstance 会被拆分成多个 Pipeline。Pipeline 是一个执行链,由一组首尾相接的 Operator 构成。
- PipelineDriver:一个 Pipeline 可以有多个实例,每个实例称为 PipelineDriver,以充分利用多个计算核心。
- Operator:算子。一个 PipelineDriver 由多个 Operator 组成。
Query Profile 的合并策略
通过分析上述结构,您可以轻易观察到,同一个 Fragment 关联的多个 FragmentInstance 在结构上具有高度相似性。同样的,归属于同一 Pipeline 的多个 PipelineDriver 也展现出类似的结构特征。为了减少 Query Profile 的体积,您可以考虑将 FragmentInstance 层进行合并,同样的方法也适用于 PipelineDriver 层。经过这样的合并处理,原本的五层结构便简化为三层,具体表现为:
- Fragment
- Pipeline
- Operator
您可以通过一个 Session 变量 pipeline_profile_level
来控制这个合并行为,其可选值有2个:
1
:合并,即三层结构。默认值。2
:不合并,即保留原始的五层结构。- 其他任何数值都会被当成默认值
1
。
通常,我们并不推荐将该参数设置为 2
。原因在于,五层结构的 Query Profile 面临众多限制,如无法借助任何工具进行可视化分析,只能依靠人工直接观察来分析。因此,除非合并过程中导致了关键信息的丢失,否则通常没有必要调整这个参数。
Metric 合并以及 MIN/MAX 值
在 FragmentInstance 和 PipelineDriver 进行合并时,需要对所有同名的指标进行合并,并记录所有并发实例中每个指标的最小值和最大值。不同种类的指标采用了不同的合并策略:
- 时间类指标求平均值。例如:
OperatorTotalTime
是所有并发实例的平均耗时。__MAX_OF_OperatorTotalTime
是所有并发实例中的最大耗时。__MIN_OF_OperatorTotalTime
是所有并发实例中的最小耗时。
- OperatorTotalTime: 2.192us
- __MAX_OF_OperatorTotalTime: 2.502us
- __MIN_OF_OperatorTotalTime: 1.882us
- 非时间类指标求和。例如:
PullChunkNum
是该指标在所有并发实例的和。__MAX_OF_PullChunkNum
是该指标在所有并发实例中的最大值。__MIN_OF_PullChunkNum
是该指标在所有并发实例中的最小值。
- PullChunkNum: 146.66K (146660)
- __MAX_OF_PullChunkNum: 24.45K (24450)
- __MIN_OF_PullChunkNum: 24.435K (24435)
- 个别没有最值的指标在所有并发实例中的值相同,例如:
DegreeOfParallelism
。
通常,MIN 和 MAX 值之间如果有明显差异,则表明数据有很大几率存在倾斜。可能的场景包括聚合和 Join 等。
- OperatorTotalTime: 2m48s
- __MAX_OF_OperatorTotalTime: 10m30s
- __MIN_OF_OperatorTotalTime: 279.170us
Query Profile 指标清单
Query Profile 包含大量查询执行详细信息的指标。在大多数情况下,您只需关注运算符的执行时间以及处理的数据量大小即可。找到瓶颈后,您就可以有针对性地解决它们。
Summary 指标
Total
描述:查询消耗的总时间,包括 Planning、Executing 以及 Profiling 阶段耗时。
Query State
描述:查询状态,可能状态包括 Finished、Error 以及 Running。
Execution Overview 指标
FrontendProfileMergeTime
描述:Query Profile 在 FE 侧的处理时间。
QueryAllocatedMemoryUsage
描述:所有计算节点,累计分配内存之和。
QueryDeallocatedMemoryUsage
描述:所有计算节点,累计释放内存之和。
QueryPeakMemoryUsagePerNode
描述:所有计算节点中,峰值内存的最大值。
QuerySumMemoryUsage
描述:所有计算节点中, 峰值内存的总和。
QueryExecutionWallTime
描述:执行的墙上时间。
QueryCumulativeCpuTime
描述:所有计算节点,累计 CPU 耗时之和。
QueryCumulativeOperatorTime
描述:所有节点耗时之和。这里是简单的线性累加,但实际上,不同的算子的执行时间可能是有重叠的。该参数作为计算算子时间占比的分母。
QueryCumulativeNetworkTime
描述:所有 Exchange 节点的网络时间之和。这里是简单的线性累加,但实际上,不同 Exchange 的执行时间可能是有重叠的。
QueryCumulativeScanTime
描述:所有 Scan 节点的 IO 时间之和。这里是简单的线性累加,但实际上,不同 Scan 的执行时间可能是有重叠的。
QueryPeakScheduleTime
描述:所有Pipeline中,ScheduleTime 指标的最大值。
QuerySpillBytes
描述:Spill 到本地磁盘的字节数量。
ResultDeliverTime
描述:传输结果的额外耗时,对于查询语句,这个参数是指数据传回客户端的时间;对于插入语句,这个参数是指数据写入到存储层的时间。
Fragment 指标
InstanceNum
描述:该 Fragment 的所有 FragmentInstance 的数量。
InstanceIds
描述:该 Fragment 的所有 FragmentInstance ID。
BackendNum
描述:参与该 Fragment 执行的 BE 的数量。
BackendAddresses
描述:参与该 Fragment 执行的所有 BE 的地址信息。
FragmentInstancePrepareTime
描述:Fragment Prepare 阶段的耗时。
InstanceAllocatedMemoryUsage
描述:该 Fragment 下所有 FragmentInstance 的累计分配内存。
InstanceDeallocatedMemoryUsage
描述:该 Fragment 下所有 FragmentInstance 的累计释放内存。
InstancePeakMemoryUsage
描述:该 Fragment 下所有 FragmentInstance 中,峰值内存的最大值。
Pipeline 指标
核心指标的关系如下图所示:
- DriverTotalTime = ActiveTime + PendingTime + ScheduleTime
- ActiveTime = ∑ OperatorTotalTime + OverheadTime
- PendingTime = InputEmptyTime + OutputFullTime + PreconditionBlockTime + PendingFinishTime
- InputEmptyTime = FirstInputEmptyTime + FollowupInputEmptyTime
DegreeOfParallelism
描述:Pipeline 执行的并行度。
TotalDegreeOfParallelism
描述:并行度之和。由于同一个 Pipeline 会在多个机器执行,这里指的是把所有并行度累加起来的值。
DriverPrepareTime
描述:执行 Prepare 的时间,该时间不包括在 DriverTotalTime 中。
DriverTotalTime
描述:Pipeline 的执行总时间。不包括 prepare 阶段的耗时。
ActiveTime
描述:Pipeline 的执行时间,包括各个算子的执行时间,以及整个框架的 Overhead,包括调用 has_output,need_input 这些方法的时间。
PendingTime
描述:Pipeline 由于各种原因,无法被调度执行而阻塞的时间。
InputEmptyTime
描述:Pipeline 由于输入队列为空而导致被阻塞的时间。
FirstInputEmptyTime
描述:Pipeline 第一次由于输入队列为空导致的阻塞时间。单独把第一次提出来是因为,第一次等待,大概率是由于 Pipeline 的依赖关系产生的。
FollowupInputEmptyTime
描述:Pipeline 后续(第二次开始)所有因为输入队列为空导致的阻塞时间。
OutputFullTime
描述:Pipeline 由于输出队列满而导致被阻塞的时间。
PreconditionBlockTime
描述:Pipeline 由于依赖条件未满足而导致被阻塞的时间。
PendingFinishTime
描述:Pipeline 由于等待异步任务结束执行而导致被阻塞的时间。
ScheduleTime
描述:Pipeline 的调度时间,即进入就绪队列,到被调度执行的这段时间。
BlockByInputEmpty
描述:由于 InputEmpty 从而被 Block 的次数。
BlockByOutputFull
描述:由于 OutputFull 从而被 Block 的次数。
BlockByPrecondition
描述:由于前置依赖未就绪从而被 Block 的次数。