GaussDB(DWS)性能调优:性能问题闭环总结(二)——表扫描慢

举报
O泡果奶~ 发表于 2023/12/29 17:34:47 2023/12/29
【摘要】 本文是作者结合实际调优案例总结梳理出来的一些常用SQL性能优化方法,主要从四个方面(表扫描慢、关联慢、聚合慢、不可向量化)来分析,本篇重点介绍表扫描慢这一部分

上一篇中已经介绍了关联慢的部分,本篇重点介绍表扫描慢这一部分
image.png

二、表扫描慢

1、因数据倾斜导致表扫描慢情况下修改分布列

业务问题

该案例并非业务问题,仅讲解使用

分析思路

众所周知,在数据仓库类型的数据库中,大表的分布列选择对于数据库和语句查询性能都有至关重要的影响。 如果表的分布列选择不当,在数据导入后有可能出现数据分布倾斜,进而导致某些磁盘的使用明显高于其他磁盘,极端情况下会导致集群只读。如下图所示,
image.png
表a为含有接近两千万数据量的HASH表,在进行表扫描时存在数据倾斜的问题:min_dn_tuple:0 max_dn_tuple:1358996,查看表a的分布列,发现分布列选择不合理
image.png
** Hash分布表的分布列选取需要满足以下基本原则:**

  • 列值应比较离散,以便数据能够均匀分布到各个DN。例如,考虑选择表的主键为分布列,如在人员信息表中选择身份证号码为分布列;
  • 在满足第一条原则的情况下尽量不要选取存在常量filter的列
  • 在满足前两条原则的情况,考虑选择查询中的连接条件为分布列,以便Join任务能够下推到DN中执行,且减少DN之间的通信数据量;
  • 支持多分布列特性,可以更好地满足数据分布的均匀性要求。
    依据上述基本原则,对表a的分布列进行修改,
alter table tablename distribute by HASH()/REPLICATION/ROUNDROBIN

修改后SQL执行计划下图所示:
image.png
SQL执行时间由原本的1s+优化为30ms左右,这是因为合理的分布列能够将数据均匀分摊到各个DN上进行分析处理,不会出现数据集中在某个DN从而引起性能瓶颈

小结

合理的分布列不但能够避免表扫描时数据倾斜情况的出现,在表关联时也能起到一定的作用(将查询条件设置为分布列,减少stream的数量

2、因脏页过多(或小CU过多)导致表扫描慢情况下清理脏页(小CU)

业务问题

SQL语句运行过程中,表扫描过慢,耗时占总耗时的一半
image.png

分析思路

查看执行计划中的Datanode Information
image.png
其中,脏页和小CU情况可以看对应表扫描那一层的CU ScanInfo情况,averCuRow代表每个CU平均数据量,如果小于1w,证明存在小CU的情况,totalDeadRows代表脏页情况
图中可以看出,该表不存在小CU情况,但存在过多脏页,此时需要对表做一下vacuum full analyze

小结

一般情况下,业务最好留个窗口期做vacuum full analyze操作,因为其执行过程中会持有八级锁,阻塞对该表的其他操作。执行过程中,可以通过查看pgxc_lock_conflicts判断是否存在锁冲突

select * from pgxc_lock_conflicts where nspname = 's' and relname = 't1'

3、表过滤慢情况下增加PCK

业务问题

SQL语句运行过程中,表扫描过慢,SQL执行时间基本都耗在表扫描上。
image.png

分析思路

PCK即局部聚簇(Partial Cluster Key, 简称PCK),其是列存储下一种通过min/max稀疏索引实现基表快速扫描的索引技术。Partial Cluster Key可以指定多列,但是一般不建议超过2列。PCK适用于列存大表点查询加速。
列存表上的一种局部聚簇技术,通过min/max 稀疏索引来实现事实表的快速过滤扫描。在导入数据时,按设置的列进行局部排序(默认每70个CU即420万行排序一次),生成的CU会聚集在一起,即CU的min,max会在一个较小的区间内。当查询时,where条件含有这些列时,可产生良好的过滤效果。
如下图所示,该SQL语句性能瓶颈在于扫描表A,表A总共13亿数据,过滤到只剩617行数据,耗时12s较长
image.png
查看执行计划中的Datanode InformationRoughCheck(根据稀疏索引排除查询不相关的数据)中的CUNone代表稀疏索引预过滤掉的元组,因此,CUNone比例越高,PCK的性能收益越明显。
image.png
如上图所示,CUNone值不大,代表当前表扫描预过滤效果并不明显,因此,将表A的过滤条件设置为PCK,实现对表A的快速扫描。
添加PCK语句

ALTER TABLE TABLENAME ADD PARTIAL CLUSTER KEY(字段名)

若已存在PCK,需要更改的话采用以下语句

SELECT conname FROM pg_constraint WHERE conrelid = 'tablename'::regclass
ALTER TABLE tablename drop CONSTRAINT constraintname

为表A增加完PCK后,其执行计划如下图所示:
image.png
表A的扫描时间有原来的12s+降低为6s+,执行效率得到了提升。同时,查看Datanode Information,CUNone值相比优化前提高了数十倍,大大降低了SQL的执行时间。
image.png

小结

PCK并不能适用于所有场景,因为其本质还是使用min/max进行查找,如果字段中数据值分布不合理的话增加PCK可能也无法提升过滤效果

4、表过滤慢情况下改写IN LIST

业务问题

SQL语句在表扫描中存在性能瓶颈,耗时较久

分析思路

in值多时过滤不下推,容易影响表扫描的效率,例如下边这个SQL语句,其in值有12个,

SELECT A.PERIOD_ID,
         IFNULL(A.CHANNEL_NAME, 'SNULL') AS DISTRIBUTOR_CHANNEL_NAME,
         SUM(A.SO_QTY_MTD) AS SO_QTY,
         SUM(DECODE(A.PERIOD_ID, 20230630, A.SO_QTY_MTD)) AS SO_QTY_ORDER
 select count(*)   FROM DM_MSS_CN_PC_REP_RP_ST_D_F A 
   INNER JOIN F_SRV_DB_DIM_PRD_D PRD 
      ON A.EXTERNAL_NAME = PRD.EXTERNAL_NAME
   WHERE 1 = 1 
     AND A.CHANNEL_ID IN ('100013388802') 
     AND A.ORG_KEY IN (10000651) 
     AND A.SALES_FLAG IN ('1', '0') 
     AND A.PERIOD_ID IN (20220731,20221031,20220930,20220831,20221130,20221231,20230131,20230228,20230430,20230331,20230531,20230630)
     AND (A.SO_QTY_MTD <> 0) -- 过滤所有日期SO_QTY为0的数据
   GROUP BY A.PERIOD_ID,
            IFNULL(A.CHANNEL_NAME, 'SNULL')

image.png
image.png
查看其执行计划,发现Pushdown Predicate Filter中并没有in值,其会影响语句的执行性能,因此,对该SQL进行改写:

A.PERIOD_ID IN (20220731,20221031,20220930,20220831,20221130)
or A.PERIOD_ID IN (20221231,20230131,20230228,20230430,20230331)
or A.PERIOD_ID IN (20230531,20230630)

保证in list数量不超过5个,使得in值能够过滤下推,改写后SQL执行计划如下图所示:
image.png
执行时间降低为600ms+,性能提高了10倍!

小结

该情况适用于8.2.0之前的版本。在8.2.0版本之前,in list默认为5个,且不可修改;8.2.0版本之后,可以修改最大个数。同时,由于存在inlist2join的转换,因此当in list值过多时不必修改为五个五个一组,优化器对自动利用join的方式进行过滤(guc参数可以控制)
qrw_inlist2join_optmode

  • 参数说明:控制是否使用inlist-to-join查询重写。
  • 参数类型:USERSET
  • 取值范围:字符串
    • disable:关闭inlist2join查询重写。
    • cost_base:基于代价的inlist2join查询重写。
    • rule_base:基于规则的inlist2join查询重写,即强制使用inlist2join查询重写。
    • 任意正整数:inlist2join查询重写阈值,即list内元素个数大于该阈值,进行inlist2join查询重写。默认值:cost_base

5、分区表过滤慢情况下分区剪枝

业务问题

SQL语句执行过程中,分区表数据量不大,但表扫描耗时较长

分析思路

image.png
image.png
查看SQL的performance执行计划,可以看到在表过滤时,对分区表的每一个分区都扫描了一遍,分区条件并没有得到合理的利用。查看SQL语句,FFR表的过滤条件存在强转,导致执行计划未分区剪枝,走了全表扫描,性能严重裂化。

FROM   RPT_TML_QM_PHONE_VENDOR_F                   FFR
INNER  JOIN RPT_TML_QM_PROD_INFO_D                PROD_INFO
ON PROD_INFO.PROD_APD_KEY = FFR.PROD_APD_KEY
AND PROD_INFO.SCD_ACTIVE_IND = 1
AND to_char(to_date(FFR.take_date),'YYYY-MM-DD') >= '2023-05-30'
AND to_char(to_date(FFR.take_date),'YYYY-MM-DD') <= '2023-06-11'    

对该处SQL进行改写优化

FROM   RPT_TML_QM_PHONE_VENDOR_F                   FFR
INNER  JOIN RPT_TML_QM_PROD_INFO_D                PROD_INFO
ON PROD_INFO.PROD_APD_KEY = FFR.PROD_APD_KEY
AND PROD_INFO.SCD_ACTIVE_IND = 1
AND FFR.take_date >= 20230530
AND FFR.take_date <= 20230611  

优化后语句执行时间降低了600ms+,执行计划如下图所示
image.png
同时,也能从下图信息看出执行过程中进行分区剪枝,降低了表扫描时扫描分区的数量,提高了表扫描效率。
image.png

小结

**分区剪枝:**分区剪枝(也称为分区消除)是CN在执行时过滤掉不需要扫描的分区,只对相关的分区进行扫描的技术。分区剪枝通常可以将查询性能提高若干数量级。
**注意:**分区表查询中表达式一侧不是单纯的分区键、而是包含分区键的表达式的Filter条件,这种类型的Filter条件是不能用来剪枝的。

6、总结

上述五个部分是梳理出来的较为常见的关联慢优化思路,表扫描慢(除脏页、小CU过多外)的优化方法归根到底就是对过滤条件进行处理

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。