单SQL性能问题快速定位手段

举报
给他提单,致命单 发表于 2024/01/09 16:37:09 2024/01/09
【摘要】 1.Analyze 1.1 收集统计信息对于SQL优化器来说准确的统计信息是能否走最优执行计划的关键。在处理大部分SQL单点问题时,第一步先尝试对所有涉及到的表做analyze,大部分情况下该SQL的执行效率均会有所优化。尤其是对于数据更新较大的表,一定要及时做analyze,这样才能保证走最优的执行计划。 1.2 统计信息采样率有时候会出现做完analyze之后执行计划仍然不准的情况,出...

1.Analyze

1.1 收集统计信息

对于SQL优化器来说准确的统计信息是能否走最优执行计划的关键。在处理大部分SQL单点问题时,第一步先尝试对所有涉及到的表做analyze,大部分情况下该SQL的执行效率均会有所优化。尤其是对于数据更新较大的表,一定要及时做analyze,这样才能保证走最优的执行计划。

1.2 统计信息采样率

有时候会出现做完analyze之后执行计划仍然不准的情况,出现这种情况可能的原因是该表的行数太多。统计信息的采样率不够。
目前收集统计信息默认采样30000行。该行数可以通过调整default_statistics_target(默认为100)这一参数来调整。当default_statistics_target为正数时,采样行数为default_statistics_target×300。也叫做绝对值采样。当default_statistics_target为负数(例如-2)时,这时采样行数为该表的总行数×0.02。我们称之为百分比采样。一般来说,当表的总行数大于160万行时,建议将采样方式修改为百分比采样。

1.3 last_analyze

1.3.1 当怀疑某张表统计信息更新不及时的时候,可以通过如下命令查询该表最后一次执行analyze的时间。
select last_analyze from PG_STAT_ALL_TABLES where schemaname = '' and relname = '';
1.3.2 有时候即便刚做完analyze也会出现行数估算不准确的问题,表现为估算行数E-rows为1或0,但是A-rows相比1为特别大的数。这时候可以尝试设置hint参数:
set enable_extrapolation_stats = off;

原因与统计信息推算时无直方图有关,具体可参考:
https://clouddevops.huawei.com/#/bug/2351077

1.4 autoanalyze

对于813及以上的版本,建议打开autoanalyze自动收集统计信息。要让数据库自动执行analyze操作需要有几个前置条件:
  1. autovacuum和autoanalyze为均on。autovacuum是autoanalyze的前置条件,只有autovacuum参数为on时,autoanalyze才会生效。
  2. autovacuum_mode为mix或analyze。analyze表示只执行autoanalyze;vacuum表示只执行autovacuum;mix表示autoanalyze和autovacuum都执行;none表示二者都不执行。
  3. 达到收集统计信息的阈值。只有表上被删除、插入或更新的数值超过设定阈值时才会对表执行analyze。影响这个阈值的有两个参数,分别为autovacuum_analyze_threshold和autovacuum_analyze_scale_factor。计算方式为当更新的行数超过
    表的总行数×autovacuum_analyze_scale_factor+autovacuum_analyze_threshold时,才会对该表执行autoanalyze。

2.EXPLAIN计划

对于大部分能跑出来的SQL,我们均可以尝试打performance执行计划来观察SQL中需要调优的瓶颈点。需要特别注意的是,performance和analyze执行计划均会实际执行SQL,所以对于INSERT或DELETE之类的语句,我们只打select部分的performance计划,对于大部分插入/删除语句来说,绝大多数问题出在查询部分。

2.1 诊断信息

在打执行计划时,也会同步打出该SQL的诊断信息。诊断信息中会提示执行计划的哪一行可能存在问题。最多的几类为:预估行数与实际行数相差过大(统计信息不准)、未正确使用索引、对大表使用了broadcast广播等。

2.2 执行计划

对于绝大多数性能问题,在观察执行计划时是自底向上看的,观察执行计划中,从哪一步开始,A-time也就是实际执行时间开始跳变,这里一般也是SQL调优的瓶颈点。
对于执行计划,我们主要关注几点信息:

2.2.1 operation算子
DWS数据库一共有扫描、连接、物化、控制四大类算子。我们在观察执行计划时,主要关注其中的扫描、连接以及控制类中的Stream算子。

其中,扫描算子可以判断出该表为行存表还是列存表,有没有走索引。该表是不是分区表,如果是分区表,扫描时有没有走分区剪枝优化。

连接算子一共有三种,分别为HashJoin、MergeJoin和NestLoopJoin。

其中NestLoopJoin的适用性最好,但是性能也最差。所以在调优时也可以针对性的让执行计划不走NestLoop,观察性能是否有所提升

set enable_nestLoop = off;
控制类算子我们主要关注其中的Stream算子。DWS的Stream算子一共有三种:

Gather Stream,主要用于CN与DN交互信息
Redistribute Stream,主要用于两表关联前对数据重分布
Broadcast Stream,与Redistribute Stream算子一样,主要也是用于两表关联前对数据重分布
而Redistribute以及Broadcast的区别在于,Redistribute是每个源节点根据连接条件计算Hash值,根据计算出的Hash值进行重分布。而Broadcast是对节点中的所有数据做广播,每一份数据都会传输到所有节点上,当需要重分布的表过大时,就会产生非常大的IO以及CPU开销。因此,我们要尽量避免对大表做Broadcast广播,在不得不走广播时,尽量让小表走广播,以节省SQL开销。

执行计划示例

下图为一个执行计划示例,最下面对customer表做顺序扫描,做Hash之后与第5行中的结果做Hash连接。而第5行中的结果是由对inventory表进行分区扫描之后broadcast重分布后得来的。
从执行计划中可以看出,第4行存在实际行数与预估行数不准的问题。同时第5行存在对大表做广播等性能不佳的执行路径。针对这种情况,第一步首先是对该SQL涉及到的两张表做analyze收集统计信息。同时针对对大表走broadcast的情况,可以设置hint参数:

set enable_broadcast = off;

来尝试规避。

3.表结构

3.1 存储结构

在如上步骤做完后,如果SQL性能还是没有明显改善,那么这时候我们就可以查询该表的表定义。表定义可以通过如下命令来查询。

\d+ schemaname.table_name;
或者
select * from pg_get_tabledef(schemaname.table_name);

3.1.1 行存or列存 分区表or非分区表

下图所示表结构为列存分区表,压缩级别为高。Hstore打开,delta表关闭(目前不建议打开列存表的delta表功能)。该表以reportingtime为分区键,一共35个分区。以vin和reportingtime做Hash分布。

查询表结构是为了让我们对这个SQL的整体性能有一个大致的判断。同样在后续的调优过程中可能也需要参考表结构来进行。
对于查询慢的场景,我们可以通过以下命令来查询涉及到的表的倾斜和脏页情况:

查倾斜:
select * from table_skewness('schemaname.tablename');
查脏页(需在业务库执行):
select * from pgxc_get_stat_dirty_tables(0,0) where relname='';

特别的,对于列存表来说,由于可能存在小批量入库情况,我们还需要查询列存表的小CU情况

select 'cstore.'||relname from pg_class where oid = (select relcudescrelid from pg_class where relname = 'xxx' and relnamespace = (select oid from pg_namespace where nspname = 'xxx'));

select 'cstore.'||relname from pg_class where oid in (select p.relcudescrelid from pg_partition p,pg_class c,pg_namespace n where c.relnamespace = n.oid and p.parentid = c.oid and c.relname = '表名' and n.nspname = '表空间名' and p.relcudescrelid != 0);

select row_count, count(*) from cstore.pg_cudesc_part_XXXXXXXX group by row_count order by 2 desc;  

下面为一个查询列存小CU的示例,意思为每一个CU都只有一条数据,一共有10185396个CU,是严重的小CU情况。需要客户整改入库方式。

3.2 分布键

分布键的选取对于SQL的执行效率有决定性的作用。一般选取表的分布键有两个原则,一个是该表根据这一列的分布是否均匀,另一个就是该分布列是否为两表做关联的条件。
例如有两张表test1,test2。两张表分别有a,b两列。表test1的分布列为a,表test2的分布列为b。此时如果查询

select * from test1 left join test2 on test1.a = test2.b;

则可以直接对两表做关联。如果是查询

select * from test1 left join test2 on test1.a = test2.a;
select * from test1 left join test2 on test1.b = test2.b;
select * from test1 left join test2 on test1.b = test2.a;

这三种情况均存在对表的重分布。
通过调整分布键的方式,可以消除查询过程中的重分布过程,如果在执行计划中发现瓶颈在重分布或者Join,则可以尝试修改分布键这种方式来对该SQL进行调优。

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

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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