GaussDB(DWS)性能调优:row_number()的PARTITION BY列倾斜场景的性能优化
【背景】
如下SQL性能慢
SELECT
T.ORDER_LINE_ID,
T.ERP_ORDER_LINE_ID,
T.DELIVERY_LINE_ID,
T.SHIP_BATCH,
T.PL_NO,
T.ASD,
T.ATA,
T.RN,
'N' AS DEL_FLAG,
CURRENT_TIMESTAMP(0) DW_LAST_UPDATE_DATE,
T.LAST_UPDATE_DATE_SOURCE
FROM(
SELECT
T.ORDER_LINE_ID,
T.ERP_ORDER_LINE_ID,
T.DELIVERY_LINE_ID,
T.SHIP_BATCH,
T.PACKING_LIST_NO AS PL_NO,
T.ASD,
T.ATA,
ROW_NUMBER() OVER(
PARTITION BY T.ORDER_LINE_ID
ORDER BY
T.SOURCE_CREATE_TIME DESC NULLS LAST,
T.DELIVERY_LINE_ID DESC NULLS LAST
) AS RN,
GREATEST(
NVL(
T.DW_LAST_MODIFIED_DATE,
TO_DATE('1900-01-01', 'YYYY-MM-DD')
),
NVL(
T.DW_LAST_MODIFIED_DATE_LINE,
TO_DATE('1900-01-01', 'YYYY-MM-DD')
)
) AS LAST_UPDATE_DATE_SOURCE
FROM FIN_DWB_ISC.DWB_ISC_SO_DELIVERY_DTL_I T /*CBG发货单*/
) T
WHERE T.RN = 1
perf摘要和SQL自诊断信息(SQL Diagnostic Information)如下,信息执行信息参见附件的《原始语句perf.txt》
SQL自诊断信息显示在做row_number()函数计算前的PARTITION BY T.ORDER_LINE_ID引入的重分布算子(Streaming(type: REDISTRIBUTE))有计算倾斜,查看对应T表的统计信息发现表fin_dwb_isc.dwb_isc_so_delivery_dtl_f的列ORDER_LINE_ID上87.6^%左右都是NULL值,这必然导致严重的计算倾斜
【优化方案】
优化的整体思路是把倾斜值单独拿出来进行row_number()排序操作,这部分数据可以忽略PARTITION BY字段(因为PARTITION BY字段值都一样),这部分可以借助DWS的vVALUE REDISTRIBUTE优化机制(详见文件尾部描述),做全局排序的优化。改写后的SQL如下
explain performance
SELECT
T.ORDER_LINE_ID,
T.ERP_ORDER_LINE_ID,
T.DELIVERY_LINE_ID,
T.SHIP_BATCH,
T.PL_NO,
T.ASD,
T.ATA,
T.RN,
'N' AS DEL_FLAG,
CURRENT_TIMESTAMP(0) DW_LAST_UPDATE_DATE,
T.LAST_UPDATE_DATE_SOURCE
FROM(
SELECT
T.ORDER_LINE_ID,
T.ERP_ORDER_LINE_ID,
T.DELIVERY_LINE_ID,
T.SHIP_BATCH,
T.PACKING_LIST_NO AS PL_NO,
T.ASD,
T.ATA,
ROW_NUMBER() OVER(
PARTITION BY T.ORDER_LINE_ID
ORDER BY
T.SOURCE_CREATE_TIME DESC NULLS LAST,
T.DELIVERY_LINE_ID DESC NULLS LAST
) AS RN,
GREATEST(
NVL(
T.DW_LAST_MODIFIED_DATE,
TO_DATE('1900-01-01', 'YYYY-MM-DD')
),
NVL(
T.DW_LAST_MODIFIED_DATE_LINE,
TO_DATE('1900-01-01', 'YYYY-MM-DD')
)
) AS LAST_UPDATE_DATE_SOURCE
FROM FIN_DWB_ISC.DWB_ISC_SO_DELIVERY_DTL_I T /*CBG发货单*/
WHERE T.ORDER_LINE_ID IS NOT NULL
UNION ALL
SELECT
T.ORDER_LINE_ID,
T.ERP_ORDER_LINE_ID,
T.DELIVERY_LINE_ID,
T.SHIP_BATCH,
T.PACKING_LIST_NO AS PL_NO,
T.ASD,
T.ATA,
ROW_NUMBER() OVER(
ORDER BY
T.SOURCE_CREATE_TIME DESC NULLS LAST,
T.DELIVERY_LINE_ID DESC NULLS LAST
) AS RN,
GREATEST(
NVL(
T.DW_LAST_MODIFIED_DATE,
TO_DATE('1900-01-01', 'YYYY-MM-DD')
),
NVL(
T.DW_LAST_MODIFIED_DATE_LINE,
TO_DATE('1900-01-01', 'YYYY-MM-DD')
)
) AS LAST_UPDATE_DATE_SOURCE
FROM FIN_DWB_ISC.DWB_ISC_SO_DELIVERY_DTL_I T /*CBG发货单*/
WHERE T.ORDER_LINE_ID IS NULL
) T
WHERE T.RN = 1
优化后耗时195s,简单执行信息如下,详细看附件的《优化后语句perf.txt》
附:VALUE REDISTRIBUTE重分布优化
对于order_line_id是NULL值的部分,因为这部分数据的order_line_id值恒为NULL,因此可以忽略PARTITION BY order_line_id字句,这样下述字句
ROW_NUMBER() OVER(
PARTITION BY T.ORDER_LINE_ID
ORDER BY T.SOURCE_CREATE_TIME DESC NULLS LAST, T.DELIVERY_LINE_ID DESC NULLS LAST
) AS RN
可以优化为
ROW_NUMBER() OVER(
ORDER BY T.SOURCE_CREATE_TIME DESC NULLS LAST, T.DELIVERY_LINE_ID DESC NULLS LAST
) AS RN
这样的语句(不带Partition by子句的rank、dense_rank、row_number)可以触发DWS特殊的VALUE REDISTRIBUTE机制,提升查询性能。
VALUE REDISTRIBUTE优化核心在于把全量数据的排序拆分为几个数据段的局部排序,整体逻辑分为以下几个步骤
- 在于先在DN本地把数据物化,提取全局的ORDER BY字段的数据分布范围
- 然后不同范围段的数据段分布到各个DN上,然后把每个DN本地排序
- 然后根据每个DN的数据分段进行全量数据的排序
- 点赞
- 收藏
- 关注作者
评论(0)