GaussDB(DWS)性能调优:性能问题闭环总结(二)——表扫描慢
上一篇中已经介绍了关联慢的部分,本篇重点介绍表扫描慢这一部分
二、表扫描慢
1、因数据倾斜导致表扫描慢情况下修改分布列
业务问题
该案例并非业务问题,仅讲解使用
分析思路
众所周知,在数据仓库类型的数据库中,大表的分布列选择对于数据库和语句查询性能都有至关重要的影响。 如果表的分布列选择不当,在数据导入后有可能出现数据分布倾斜,进而导致某些磁盘的使用明显高于其他磁盘,极端情况下会导致集群只读。如下图所示,
表a为含有接近两千万数据量的HASH表,在进行表扫描时存在数据倾斜的问题:min_dn_tuple:0 max_dn_tuple:1358996,查看表a的分布列,发现分布列选择不合理
** Hash分布表的分布列选取需要满足以下基本原则:**
- 列值应比较离散,以便数据能够均匀分布到各个DN。例如,考虑选择表的主键为分布列,如在人员信息表中选择身份证号码为分布列;
- 在满足第一条原则的情况下尽量不要选取存在常量filter的列;
- 在满足前两条原则的情况,考虑选择查询中的连接条件为分布列,以便Join任务能够下推到DN中执行,且减少DN之间的通信数据量;
- 支持多分布列特性,可以更好地满足数据分布的均匀性要求。
依据上述基本原则,对表a的分布列进行修改,
alter table tablename distribute by HASH()/REPLICATION/ROUNDROBIN
修改后SQL执行计划下图所示:
SQL执行时间由原本的1s+优化为30ms左右,这是因为合理的分布列能够将数据均匀分摊到各个DN上进行分析处理,不会出现数据集中在某个DN从而引起性能瓶颈
小结
合理的分布列不但能够避免表扫描时数据倾斜情况的出现,在表关联时也能起到一定的作用(将查询条件设置为分布列,减少stream的数量)
2、因脏页过多(或小CU过多)导致表扫描慢情况下清理脏页(小CU)
业务问题
SQL语句运行过程中,表扫描过慢,耗时占总耗时的一半
分析思路
查看执行计划中的Datanode Information,
其中,脏页和小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执行时间基本都耗在表扫描上。
分析思路
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较长
查看执行计划中的Datanode Information,RoughCheck(根据稀疏索引排除查询不相关的数据)中的CUNone代表稀疏索引预过滤掉的元组,因此,CUNone比例越高,PCK的性能收益越明显。
如上图所示,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后,其执行计划如下图所示:
表A的扫描时间有原来的12s+降低为6s+,执行效率得到了提升。同时,查看Datanode Information,CUNone值相比优化前提高了数十倍,大大降低了SQL的执行时间。
小结
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')
查看其执行计划,发现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执行计划如下图所示:
执行时间降低为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语句执行过程中,分区表数据量不大,但表扫描耗时较长
分析思路
查看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+,执行计划如下图所示
同时,也能从下图信息看出执行过程中进行分区剪枝,降低了表扫描时扫描分区的数量,提高了表扫描效率。
小结
**分区剪枝:**分区剪枝(也称为分区消除)是CN在执行时过滤掉不需要扫描的分区,只对相关的分区进行扫描的技术。分区剪枝通常可以将查询性能提高若干数量级。
**注意:**分区表查询中表达式一侧不是单纯的分区键、而是包含分区键的表达式的Filter条件,这种类型的Filter条件是不能用来剪枝的。
6、总结
上述五个部分是梳理出来的较为常见的关联慢优化思路,表扫描慢(除脏页、小CU过多外)的优化方法归根到底就是对过滤条件进行处理。
- 点赞
- 收藏
- 关注作者
评论(0)