背景

对于两个大表关联的场景,如果过滤条件的列值,存在高度倾斜,可以考虑根据反向滤值,进行过滤操作,减少连接的CPU时间。

数据准备

-- 状态表 tp01_state 记录 大表tp01 记录的多种状态 

kingbase=# select count(*) from tp01;
count
----------
10000000
(1 行记录) --只有一个高度倾斜的列值
kingbase=# select issuc,count(*) from tp01_state group by issuc order by issuc; issuc | count
-------+---------
N | 100
Y | 9999900
(2 行记录) --有多个高度倾斜的列值
kingbase=# select istype, count(*) from tp01_state group by istype order by istype;
istype | count
--------+---------
A | 100
C | 8999700
G | 100
M | 1000000
W | 100
(5 行记录)

查询issuc='Y'数据

标准语句

多数数据匹配issuc='Y'条件,执行计划就是两个大表,进行hashjoin。

select * from tp01 where id in (select id from tp01_state where issuc = 'Y');
--或者
select * from tp01 where exists (select 1 from tp01_state where id = tp01.id and issuc = 'Y'); -- QUERY PLAN
Hash Semi Join (cost=338555.00..1033383.15 rows=10000000 width=241) (actual time=2398.867..5889.537 rows=9999900 loops=1)
Hash Cond: (tp01.id = tp01_state.id)
-> Seq Scan on tp01 (cost=0.00..444828.12 rows=10000012 width=241) (actual time=0.005..611.596 rows=10000000 loops=1)
-> Hash (cost=213555.00..213555.00 rows=10000000 width=4) (actual time=2384.857..2384.858 rows=9999900 loops=1)
Buckets: 16777216 Batches: 1 Memory Usage: 482631kB
-> Seq Scan on tp01_state (cost=0.00..213555.00 rows=10000000 width=4) (actual time=0.011..775.853 rows=9999900 loops=1)
Filter: (issuc = 'Y'::text)
Rows Removed by Filter: 100
Planning Time: 0.186 ms
Execution Time: 6137.233 ms

问题:tp_state 数据千万级,两张表关联必然要消耗大量的CPU资源。可以看到,主要的时间消耗在hash join上

优化1:使用NOT IN 代替 IN

因为只有少量数据,匹配issuc='Y'反向条件,使用not in 减少大表的过滤操作。

select * from tp01 where tp01.id not in (select id from tp01_state where issuc <> 'Y' or issuc is null);

-- QUERY PLAN
Seq Scan on tp01 (cost=213555.00..683383.15 rows=5000006 width=241) (actual time=517.554..1629.795 rows=9999900 loops=1)
Filter: (NOT (hashed SubPlan 1))
Rows Removed by Filter: 100
SubPlan 1
-> Seq Scan on tp01_state (cost=0.00..213555.00 rows=1 width=4) (actual time=271.143..517.503 rows=100 loops=1)
Filter: (issuc <> 'Y'::text)
Rows Removed by Filter: 9999900
Planning Time: 0.087 ms
Execution Time: 1870.376 ms

修改后的SQL,虽然使用了filter 方式,但由于SubPlan 1 结果集很小,效率还是非常高效的。

优化2:使用not between 代替 <>

not between 操作可以使用索引,就可以减少子查询的执行时间。

select *
from tp01
where tp01.id not in (select id from tp01_state where issuc not between 'Y' and 'Y' or issuc is null); -- QUERY PLAN
Seq Scan on tp01 (cost=17.35..469845.50 rows=5000006 width=241) (actual time=0.098..1109.085 rows=9999900 loops=1)
Filter: (NOT (hashed SubPlan 1))
Rows Removed by Filter: 100
SubPlan 1
-> Bitmap Heap Scan on tp01_state (cost=13.33..17.34 rows=1 width=4) (actual time=0.035..0.045 rows=100 loops=1)
Recheck Cond: ((issuc < 'Y'::text) OR (issuc > 'Y'::text) OR (issuc IS NULL))
Heap Blocks: exact=2
-> BitmapOr (cost=13.33..13.33 rows=1 width=0) (actual time=0.028..0.030 rows=0 loops=1)
-> Bitmap Index Scan on tp01_state_issuc (cost=0.00..4.44 rows=1 width=0) (actual time=0.020..0.020 rows=100 loops=1)
Index Cond: (issuc < 'Y'::text)
-> Bitmap Index Scan on tp01_state_issuc (cost=0.00..4.44 rows=1 width=0) (actual time=0.007..0.007 rows=0 loops=1)
Index Cond: (issuc > 'Y'::text)
-> Bitmap Index Scan on tp01_state_issuc (cost=0.00..4.44 rows=1 width=0) (actual time=0.001..0.001 rows=0 loops=1)
Index Cond: (issuc IS NULL)
Planning Time: 0.109 ms
Execution Time: 1349.526 ms

查询istype in ('C','M')数据

标准语句

多数数据匹配istype in ('C','M')条件,执行计划就是两个大表,进行hashjoin。

explain analyze
select *
from tp01
where id in (select id from tp01_state where istype in ('C', 'M')); -- QUERY PLAN
Hash Semi Join (cost=307305.11..927445.94 rows=7500009 width=241) (actual time=2848.058..6398.654 rows=9999700 loops=1)
Hash Cond: (tp01.id = tp01_state.id)
-> Seq Scan on tp01 (cost=0.00..444828.12 rows=10000012 width=241) (actual time=0.005..613.502 rows=10000000 loops=1)
-> Hash (cost=213555.00..213555.00 rows=7500009 width=4) (actual time=2840.972..2840.972 rows=9999700 loops=1)
Buckets: 16777216 (originally 8388608) Batches: 1 (originally 1) Memory Usage: 482624kB
-> Seq Scan on tp01_state (cost=0.00..213555.00 rows=7500009 width=4) (actual time=0.034..1032.910 rows=9999700 loops=1)
Filter: (istype = ANY ('{C,M}'::text[]))
Rows Removed by Filter: 300
Planning Time: 0.193 ms
Execution Time: 6646.452 ms

优化1:使用NOT IN 代替 IN

因为只有少量数据,匹配istype in ('C','M')反向条件,使用not in 减少大表的过滤操作。

select *
from tp01
where id not in (select id from tp01_state where istype not in ('C', 'M') or istype is null ); -- QUERY PLAN
Seq Scan on tp01 (cost=175497.98..645326.13 rows=5000006 width=241) (actual time=778.116..2699.271 rows=9999700 loops=1)
Filter: (NOT (hashed SubPlan 1))
Rows Removed by Filter: 300
SubPlan 1
-> Seq Scan on tp01_state (cost=0.00..169248.00 rows=2499991 width=4) (actual time=0.006..767.589 rows=300 loops=1)
Filter: ((istype <> ALL ('{C,M}'::text[])) OR (istype IS NULL))
Rows Removed by Filter: 9999700
Planning Time: 0.101 ms
Execution Time: 2934.265 ms

优化2:使用not between 代替 <>

not between 操作根据选择率最佳的列值,使用索引,就可以减少子查询的执行时间。

select *
from tp01
where id not in (select id from tp01_state
where (istype not between 'C' and 'C' and istype not between 'M' and 'M') or istype is null); -- QUERY PLAN
Seq Scan on tp01 (cost=106721.48..576549.63 rows=5000006 width=241) (actual time=223.295..1862.006 rows=9999700 loops=1)
Filter: (NOT (hashed SubPlan 1))
Rows Removed by Filter: 300
SubPlan 1
-> Bitmap Heap Scan on tp01_state (cost=22927.80..104507.84 rows=885454 width=4) (actual time=58.615..220.275 rows=300 loops=1)
Recheck Cond: (((istype < 'C'::text) OR (istype > 'C'::text)) OR (istype IS NULL))
Filter: ((((istype < 'C'::text) OR (istype > 'C'::text)) AND ((istype < 'M'::text) OR (istype > 'M'::text))) OR (istype IS NULL))
Rows Removed by Filter: 1000000
Heap Blocks: exact=4428
-> BitmapOr (cost=22927.80..22927.80 rows=981652 width=0) (actual time=58.266..58.268 rows=0 loops=1)
-> BitmapOr (cost=22701.99..22701.99 rows=981652 width=0) (actual time=58.262..58.263 rows=0 loops=1)
-> Bitmap Index Scan on tp01_state_istype (cost=0.00..5.69 rows=167 width=0) (actual time=0.026..0.027 rows=100 loops=1)
Index Cond: (istype < 'C'::text)
-> Bitmap Index Scan on tp01_state_istype (cost=0.00..22253.58 rows=981486 width=0) (actual time=58.235..58.235 rows=1000200 loops=1)
Index Cond: (istype > 'C'::text)
-> Bitmap Index Scan on tp01_state_istype (cost=0.00..4.44 rows=1 width=0) (actual time=0.004..0.004 rows=0 loops=1)
Index Cond: (istype IS NULL)
Planning Time: 0.350 ms
Execution Time: 2099.544 ms

优化3:使用<和>的范围条件组合,代替not between

将多个not between条件,分解成范围条件组合,充分利用索引,减少filter操作。

select *
from tp01
where id not in (
select id
from tp01_state
where (istype < 'C')
or (istype > 'C' and istype < 'M')
or (istype > 'M')
or istype is null); -- QUERY PLAN
Seq Scan on tp01 (cost=350.11..470178.26 rows=5000006 width=241) (actual time=0.142..1099.829 rows=9999700 loops=1)
Filter: (NOT (hashed SubPlan 1))
Rows Removed by Filter: 300
SubPlan 1
-> Bitmap Heap Scan on tp01_state (cost=8.60..349.28 rows=334 width=4) (actual time=0.067..0.091 rows=300 loops=1)
Recheck Cond: ((istype < 'C'::text) OR ((istype > 'C'::text) AND (istype < 'M'::text)) OR (istype > 'M'::text) OR (istype IS NULL))
Heap Blocks: exact=2
-> BitmapOr (cost=8.60..8.60 rows=334 width=0) (actual time=0.058..0.060 rows=0 loops=1)
-> Bitmap Index Scan on tp01_state_istype (cost=0.00..2.69 rows=167 width=0) (actual time=0.019..0.019 rows=100 loops=1)
Index Cond: (istype < 'C'::text)
-> Bitmap Index Scan on tp01_state_istype (cost=0.00..1.45 rows=1 width=0) (actual time=0.024..0.024 rows=100 loops=1)
Index Cond: ((istype > 'C'::text) AND (istype < 'M'::text))
-> Bitmap Index Scan on tp01_state_istype (cost=0.00..2.69 rows=167 width=0) (actual time=0.014..0.014 rows=100 loops=1)
Index Cond: (istype > 'M'::text)
-> Bitmap Index Scan on tp01_state_istype (cost=0.00..1.44 rows=1 width=0) (actual time=0.001..0.001 rows=0 loops=1)
Index Cond: (istype IS NULL)
Planning Time: 0.183 ms
Execution Time: 1340.184 ms

总结

查询优化的宗旨,是更少的数据量和更少计算量,不要摒弃 not in这样不易优化的操作符。

SQL调优系列--数据严重倾斜的连接优化的更多相关文章

  1. Oracle SQL调优系列之SQL Monitor Report

    @ 目录 1.SQL Monitor简介 2.捕捉sql的前提 3.SQL Monitor 参数设置 4.SQL Monitor Report 4.1.SQL_ID获取 4.2.Text文本格式 4. ...

  2. HiveSql调优系列之Hive严格模式,如何合理使用Hive严格模式

    目录 综述 1.严格模式 1.1 参数设置 1.2 查看参数 1.3 严格模式限制内容及对应参数设置 2.实际操作 2.1 分区表查询时必须指定分区 2.2 order by必须指定limit 2.3 ...

  3. SQL Server调优系列基础篇(并行运算总结)

    前言 上三篇文章我们介绍了查看查询计划的方式,以及一些常用的连接运算符.联合运算符的优化技巧. 本篇我们分析SQL Server的并行运算,作为多核计算机盛行的今天,SQL Server也会适时调整自 ...

  4. SQL Server调优系列基础篇

    前言 关于SQL Server调优系列是一个庞大的内容体系,非一言两语能够分析清楚,本篇先就在SQL 调优中所最常用的查询计划进行解析,力图做好基础的掌握,夯实基本功!而后再谈谈整体的语句调优. 通过 ...

  5. SQL Server调优系列基础篇(常用运算符总结——三种物理连接方式剖析)

    前言 上一篇我们介绍了如何查看查询计划,本篇将介绍在我们查看的查询计划时的分析技巧,以及几种我们常用的运算符优化技巧,同样侧重基础知识的掌握. 通过本篇可以了解我们平常所写的T-SQL语句,在SQL ...

  6. SQL Server调优系列基础篇(联合运算符总结)

    前言 上两篇文章我们介绍了查看查询计划的方式,以及一些常用的连接运算符的优化技巧,本篇我们总结联合运算符的使用方式和优化技巧. 废话少说,直接进入本篇的主题. 技术准备 基于SQL Server200 ...

  7. SQL Server调优系列基础篇(并行运算总结篇二)

    前言 上一篇文章我们介绍了查看查询计划的并行运行方式. 本篇我们接着分析SQL Server的并行运算. 闲言少叙,直接进入本篇的正题. 技术准备 同前几篇一样,基于SQL Server2008R2版 ...

  8. SQL Server调优系列基础篇(索引运算总结)

    前言 上几篇文章我们介绍了如何查看查询计划.常用运算符的介绍.并行运算的方式,有兴趣的可以点击查看. 本篇将分析在SQL Server中,如何利用先有索引项进行查询性能优化,通过了解这些索引项的应用方 ...

  9. SQL Server调优系列基础篇(子查询运算总结)

    前言 前面我们的几篇文章介绍了一系列关于运算符的介绍,以及各个运算符的优化方式和技巧.其中涵盖:查看执行计划的方式.几种数据集常用的连接方式.联合运算符方式.并行运算符等一系列的我们常见的运算符.有兴 ...

  10. SQL Server调优系列进阶篇(查询优化器的运行方式)

    前言 前面我们的几篇文章介绍了一系列关于运算符的基础介绍,以及各个运算符的优化方式和技巧.其中涵盖:查看执行计划的方式.几种数据集常用的连接方式.联合运算符方式.并行运算符等一系列的我们常见的运算符. ...

随机推荐

  1. 【framework】Activity启动流程

    1 前言 ATMS启动流程 介绍了 ActivityTaskManagerService(ATMS)的启动和初始化流程,本文将介绍 Activity 的启动流程.由于 Activity 启动流程复杂, ...

  2. 利用javax.validation实现对bean属性校验

    1.使用场景介绍 controller层对传入的bean的属性进行非空.属性长度等合法性的校验. 传统的方式是我们要自己写if-else-去判断,比较麻烦. 2.实现效果 3.代码介绍 代码结构: p ...

  3. Flask AttributeError 'Request' object has no attribute 'is_xhr'解决办法

    flask版本0.12.5 运行时会报以上错误,原因是因为werkzeug的版本大于等于1.x版本. 解决办法,降低werkzeug的版本即可 pip uninstall Werkzeug pip i ...

  4. MySQL基础3-数据库增删改操作

    一.SQL通用语法 SQL 语法可以单行或者多行书写,以分号结尾. SQL 语句可以使用空格/缩进来增强语句的可读性. MySQL 数据库的 SQL 语句不区分大小写,关键字建议使用大写. 注释: 单 ...

  5. 【Azure 应用服务】在Azure Funciton中使用Powershell脚本函数,需要存储一些变量值如何解决?

    问题描述 使用Azure Function创建Powershell脚本来执行函数,在使用中需要存储一些不重要的参数.一般情况,存储的问题都是交给DB,Storage等来解决.但是有没有一种简单的办法呢 ...

  6. 【Azure 存储服务】Azure Blob下面是否可以创建子文件夹

    问题描述 如何在Azure Storage Account(存储账户) 门户上为 Container 创建子文件夹? 问题解决 经验证,没有办法在门户上直接创建文件夹,不过可以使用Azure Stor ...

  7. 分组聚合不再难:Pandas groupby使用指南

    处理大量数据时,经常需要对数据进行分组和汇总,groupby为我们提供了一种简洁.高效的方式来实现这些操作,从而简化了数据分析的流程. 1. 分组聚合是什么 分组是指根据一个或多个列的值将数据分成多个 ...

  8. Java 异常处理(2) : 异常处理的方式二:throws + 异常类型

    1 package com.bytezero.throwable; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 impor ...

  9. Java //100以内的质数的输出(从2开始,到这个数-1结束为止,都不能被这个数本身整除)+优化

    1 //100以内的质数的输出(从2开始,到这个数-1结束为止,都不能被这个数本身整除) 2 boolean isFlag = true; //标识i是否被j除尽,修改其值 3 4 for(int i ...

  10. postgreSql最佳配置详解(connection 申请、回收策略)

    一.引子 合理配置一个应用的数据库参数,使其运行良好,这很重要.本文以某务中台的生产环境为例,从Apollo上拔下来一套配置,分析是否合理. 二.MybatisPlus配置 由于我们使用Apollo配 ...