背景

对于两个大表关联的场景,如果过滤条件的列值,存在高度倾斜,可以考虑根据反向滤值,进行过滤操作,减少连接的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. letcode-括号生成

    递归大法,空间换时间 就是记录左右括号数,一旦右括号数大于左括号数,退出. 当左右括号数相等,且等于n则为合法解. 使用char数组取代StringBuilder可以减少内存使用,这样每次进行回溯时不 ...

  2. win32 - MultiByteToWideChar的示例

    该函数经常被用来处理UTF-8和ANSI格式的字符串,将它们转换为宽字节(UTF-16) #include <iostream> #include <Windows.h> #i ...

  3. 【Android 逆向】【攻防世界】easyjava

    1. apk 安装到手机,提示输入flag 2. jadx 打开apk看看 private static char a(String str, b bVar, a aVar) { return aVa ...

  4. 大众点评-CAT监控平台

    前言 我们禀着发现问题,解决问题的方针,针对后台诸多的服务,如何实时监控接口性能和访问频率,还要统计大盘信息?CAT作为大众点评开源的系统监控平台项目,下面就介绍一下CAT平台的搭建步骤. CAT作为 ...

  5. 【Azure 应用服务】[App Service For Linux(Function) ] Python ModuleNotFoundError: No module named 'MySQLdb'

    问题描述 在使用Azure Function创建新的Python Function时,使用MySQLdb连接数据库时候出现 ModuleNotFoundError: No module named ' ...

  6. Java 接口的使用

    1 package com.bytezreo.interfacetest; 2 3 /** 4 * 5 * @Description 接口的使用 6 * @author Bytezero·zhengl ...

  7. Python 潮流周刊第 41 期(摘要),赠书5本

    本周刊由 Python猫 出品,精心筛选国内外的 250+ 信息源,为你挑选最值得分享的文章.教程.开源项目.软件工具.播客和视频.热门话题等内容.愿景:帮助所有读者精进 Python 技术,并增长职 ...

  8. 13 种在 JavaScript 中删除/过滤数组的方法【转】

    英文 | https://javascript.plainenglish.io/13-methods-to-remove-filter-an-item-in-an-array-and-array-of ...

  9. 一个简单的HTTP服务器的实现

    我们继续我们的HTTP服务器的实现(使用别的代码来实现), 这个HTTP服务器的实现,我们主要就是关注TCP服务器中的recv还有send的处理. 首先,看一下HTTP,我们在用浏览器访问我们的TCP ...

  10. day12-面向对象03

    面向对象03 10.抽象类 abstract修饰符可以用来修饰方法也可以修饰类,如果修饰方法,那么该方法就是抽象方法:如果修饰类,那么该类就是抽象类 抽象类中可以没有抽象方法,但是有抽象方法的类一定要 ...