
# 数据倾斜导致SQL执行慢，大表SQL执行无结果
#### 问题现象
某场景下SQL执行慢，涉及大表的SQL执行不出来结果。
#### 原因分析
DWS支持Hash、REPLICATION和ROUNDROBIN（8.1.2集群及以上版本支持ROUNDROBIN）分布方式。如果创建了Hash分布的表，未指定分布键，则选择表的第一列作为分布键，这种情况就可能存在倾斜。倾斜造成以下负面影响：
- SQL的性能会非常差，因为数据只分布在部分DN，那么SQL运行的时候就只有部分DN参与计算，没有发挥分布式的优势。
- 会导致资源倾斜，尤其是磁盘。可能部分磁盘的空间已经接近极限，但是其他磁盘利用率很低。
- 可能出现部分节点CPU过高等问题。
 
#### 分析过程
1. 登录[DWS控制台](https://console.huaweicloud.com/dws)。在"集群列表"页面，找到需要查看监控的集群。在指定集群所在行的"操作"列，单击"监控面板"。选择"监控 \> 节点监控 \> 磁盘"，查看磁盘使用率。
   
   ![](https://support.huaweicloud.com/trouble-dws/public_sys-resources/note_3.0-zh-cn.png)
   各个数据磁盘的利用率，会有不均衡的现象。正常情况下，利用率最高和利用率最低的磁盘空间相差不大，如果磁盘利用率相差超过了5%就要注意是不是有资源倾斜的情况。
   
   
2. 连接数据库，通过等待视图查看作业的运行情况，发现作业总是等待部分DN或者个别DN。 
   ```
   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;
   ```
   
   
3. 执行慢语句的explain performance显示，发现各个DN的基表scan的时间和行数不均衡。 
   ```
   explain performance select avg(ss_wholesale_cost) from store_sales;
   ```
   ![](https://support.huaweicloud.com/trouble-dws/figure/zh-cn_image_0000001163552957.png "点击放大")
   - 基表scan的时间：最快的DN耗时5ms，最慢的DN耗时1173ms。
   
   - 数据分布情况：某些DN有22831616行，其他DN都是0行，数据有严重倾斜。
   
   
   ![](https://support.huaweicloud.com/trouble-dws/figure/zh-cn_image_0000001119917890.png "点击放大")
   
   
4. 通过倾斜检查接口可以发现数据倾斜。 
   ```
   SELECT table_skewness('store_sales');
   ```
   ![](https://support.huaweicloud.com/trouble-dws/figure/zh-cn_image_0000001163513637.png "点击放大")
   ```
   SELECT table_distribution('public','store_sales');
   ```
   ![](https://support.huaweicloud.com/trouble-dws/figure/zh-cn_image_0000001163553799.png "点击放大")
   
   

5. 通过资源监控发现，个别节点的CPU和I/O明显比其他节点高。
 
#### 处理方法
**如何找到倾斜的表：**
1. 数据库中表个数少于1万的场景下，可直接使用倾斜视图查询当前库内所有表的数据倾斜情况。
   ```
   SELECT * FROM pgxc_get_table_skewness ORDER BY totalsize DESC;
   ```
   
2. 数据库中表个数非常多（至少大于1万）的场景，因[PGXC_GET_TABLE_SKEWNESS](https://support.huaweicloud.com/devg-dws/dws_04_0805.html)视图涉及全库查并计算非常全面的倾斜字段，所以可能会花费比较长的时间（小时级），建议参考PGXC_GET_TABLE_SKEWNESS视图定义，执行以下操作：
   - 8.1.2及之前集群版本中使用[table_distribution()](https://support.huaweicloud.com/sqlreference-dws/dws_06_0062.html)函数自定义输出，减少输出列进行计算优化，例如：
     ```
     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()](https://support.huaweicloud.com/sqlreference-dws/dws_06_0062.html)函数，全库查询所有表的数据倾斜情况。全库表查询时，gs_table_distribution()函数优于table_distribution()函数；在大集群大数据量场景下，如果进行全库表查询，建议优先使用gs_table_distribution()函数。
     ```
     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;
     ```
     ![](https://support.huaweicloud.com/trouble-dws/public_sys-resources/note_3.0-zh-cn.png)
     使用如下语句可快速查询到大表：
     ```
     SELECT schemaname||'.'||tablename as table, sum(dnsize) as size FROM gs_table_distribution() group by 1 order by 2 desc limit 10;
     ```
     使用如下语句可快速查询表的倾斜率：
     ```
     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;
     ```
     
    
 
**表的分布键的选择方法：**
1. 如果此列的distinct值比较大，并且没有明显的数据倾斜，也可以把多列定义成分布列。
   查看distinct的大小：
   ```
   SELECT count(distinct column1) FROM table;
   ```
   查看数据是否存在倾斜：
   ```
   SELECT count(*) cnt, column1 FROM table group by column1 order by cnt limit 100;
   ```
   
2. 选用经常做JOIN或group by的列，可以减少STREAM运算。
3. 不推荐以下分布键选择方式：
   1. 分布列用默认值（第一列）。
   
   2. 分布列用sequence自增生成。
   
   3. 分布列用随机数生成（除非任意列，或者任意两列的组合做分布键都是倾斜的，一般不选用这种方法）。
    
 
