数据倾斜导致SQL执行慢,大表SQL执行无结果
问题现象
某场景下SQL执行慢,涉及大表的SQL执行不出来结果。
原因分析
GaussDB(DWS)支持Hash、REPLICATION和ROUNDROBIN(8.1.2集群及以上版本支持ROUNDROBIN)分布方式。如果创建了Hash分布的表,未指定分布键,则选择表的第一列作为分布键,这种情况就可能存在倾斜。倾斜造成以下负面影响:
- SQL的性能会非常差,因为数据只分布在部分DN,那么SQL运行的时候就只有部分DN参与计算,没有发挥分布式的优势。
- 会导致资源倾斜,尤其是磁盘。可能部分磁盘的空间已经接近极限,但是其他磁盘利用率很低。
- 可能出现部分节点CPU过高等问题。
分析过程
- 登录GaussDB(DWS)管理控制台。在“集群管理”页面,找到需要查看监控的集群。在指定集群所在行的“操作”列,单击“监控面板”。选择“监控 > 节点监控 > 磁盘”,查看磁盘使用率。
各个数据磁盘的利用率,会有不均衡的现象。正常情况下,利用率最高和利用率最低的磁盘空间相差不大,如果磁盘利用率相差超过了5%就要注意是不是有资源倾斜的情况。
- 连接数据库,通过等待视图查看作业的运行情况,发现作业总是等待部分DN或者个别DN。
1
SELECT wait_status, count(*) as cnt FROM pgxc_thread_wait_status WHERE wait_status not like '%cmd%' AND wait_status not like '%none%' and wait_status not like '%quit%' group by 1 order by 2 desc;
- 执行慢语句的explain performance显示,发现各个DN的基表scan的时间和行数不均衡。
1
explain performance select avg(ss_wholesale_cost) from store_sales;
- 基表scan的时间:最快的DN耗时5ms,最慢的DN耗时1173ms。
- 数据分布情况:某些DN有22831616行,其他DN都是0行,数据有严重倾斜。
- 通过倾斜检查接口可以发现数据倾斜。
1
SELECT table_skewness('store_sales');
1
SELECT table_distribution('public','store_sales');
- 通过资源监控发现,个别节点的CPU/IO明显比其他节点高。
处理方法
- 数据库中表个数少于1W的场景下,可直接使用倾斜视图查询当前库内所有表的数据倾斜情况。
1
SELECT * FROM pgxc_get_table_skewness ORDER BY totalsize DESC;
- 数据库中表个数非常多(至少大于1W)的场景,因PGXC_GET_TABLE_SKEWNESS视图涉及全库查并计算非常全面的倾斜字段,所以可能会花费比较长的时间(小时级),建议参考PGXC_GET_TABLE_SKEWNESS视图定义,执行以下操作:
- 8.1.2及之前集群版本中使用table_distribution()函数自定义输出,减少输出列进行计算优化,例如:
1 2 3 4 5 6
SELECT schemaname,tablename,max(dnsize) AS maxsize, min(dnsize) AS minsize FROM pg_catalog.pg_class c INNER JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace INNER JOIN pg_catalog.table_distribution() s ON s.schemaname = n.nspname AND s.tablename = c.relname INNER JOIN pg_catalog.pgxc_class x ON c.oid = x.pcrelid AND x.pclocatortype = 'H' GROUP BY schemaname,tablename;
- 8.1.3及以上集群版本中支持使用gs_table_distribution()函数,全库查询所有表的数据倾斜情况。全库表查询时,gs_table_distribution()函数优于table_distribution()函数;在大集群大数据量场景下,如果进行全库表查询,建议优先使用gs_table_distribution()函数。
1 2 3 4 5 6
SELECT schemaname,tablename,max(dnsize) AS maxsize, min(dnsize) AS minsize FROM pg_catalog.pg_class c INNER JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace INNER JOIN pg_catalog.gs_table_distribution() s ON s.schemaname = n.nspname AND s.tablename = c.relname INNER JOIN pg_catalog.pgxc_class x ON c.oid = x.pcrelid AND x.pclocatortype = 'H' GROUP BY schemaname,tablename;
使用如下语句可快速查询到大表:
1
SELECT schemaname||'.'||tablename as table, sum(dnsize) as size FROM gs_table_distribution() group by 1 order by 2 desc limit 10;
使用如下语句可快速查询表的倾斜率:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
WITH skew AS ( SELECT schemaname, tablename, pg_catalog.sum(dnsize) AS totalsize, pg_catalog.avg(dnsize) AS avgsize, pg_catalog.max(dnsize) AS maxsize, pg_catalog.min(dnsize) AS minsize, (pg_catalog.max(dnsize) - pg_catalog.min(dnsize)) AS skewsize, pg_catalog.stddev(dnsize) AS skewstddev FROM pg_catalog.pg_class c INNER JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace INNER JOIN pg_catalog.gs_table_distribution() s ON s.schemaname = n.nspname AND s.tablename = c.relname INNER JOIN pg_catalog.pgxc_class x ON c.oid = x.pcrelid AND x.pclocatortype IN('H', 'N') GROUP BY schemaname,tablename ) SELECT schemaname, tablename, totalsize, avgsize::numeric(1000), (maxsize/totalsize)::numeric(4,3) AS maxratio, (minsize/totalsize)::numeric(4,3) AS minratio, skewsize, (skewsize/avgsize)::numeric(4,3) AS skewratio, skewstddev::numeric(1000) FROM skew WHERE totalsize > 0;
- 8.1.2及之前集群版本中使用table_distribution()函数自定义输出,减少输出列进行计算优化,例如:
- 如果此列的distinct值比较大,并且没有明显的数据倾斜,也可以把多列定义成分布列。
查看数据是否存在倾斜:
1
SELECT count(*) cnt, column1 FROM table group by column1 order by cnt limit 100;
- 选用经常做JOIN或group by的列,可以减少STREAM运算。
- 不推荐以下分布键选择方式:
- 分布列用默认值(第一列)。
- 分布列用sequence自增生成。
- 分布列用随机数生成(除非任意列,或者任意两列的组合做分布键都是倾斜的,一般不选用这种方法)。