(Oracle)SQL优化案例:大表hash连接优化

真实SQL优化案例

为避免项目隐私泄露;

本篇文章所有表名、字段名,包括执行计划内的对象名称都做了处理。 

本篇文章是将速度再10秒左右的SQL优化到1.5s左右;

因为没有优化到1s以下,所以可能还存在更优优化方法;

但其中涉及的优化技巧,可以供您赏析。

目录

项目场景

SQL分析

优化方案

优化总结


项目场景

甲方反应如下SQL执行缓慢,需要10秒左右才能执行完。

SELECT 
COUNT(1) AS CNT
FROM LA 
LEFT JOIN  IMO ON LA.ID = IMO.ID
WHERE IMO.SOURCE_ID IS NULL 
AND IMO.STATUS_ID NOT IN ('080','085')

该SQL的查询结果是 2498900 。 

SQL分析

  • SQL本身逻辑分析

Ⅰ:大表 

上述SQL的查询结果是 2498900 ;说明两张表本身是大表。经过如下查询,确实是两张大表,LA大小是0.9G,IMO大小是2.16G。

也就是说,这是两张大表进行关联查询。

--0.9G
select 
round(ds.bytes/1024/1024/1024,2) as tablesize,
ds.*
from dba_segments ds where segment_name = 'LA'--2.16G
select 
round(ds.bytes/1024/1024/1024,2) as tablesize,
ds.*
from dba_segments ds where segment_name = 'IMO'

Ⅱ:WHERE条件

WHERE条件中只有两个谓词字段:

IMO.SOURCE_ID IS NULL 、IMO.STATUS_ID,这两个其实在业务逻辑上只是作废状态判断。也就是说过滤掉的数据不会超过总数据量的20%,相当于会统计LA表的80%数据

IMO.STATUS_ID NOT IN ('080','085'),这个条件使用了NOT IN,也就意味着即使在IMO.STATUS_ID 字段上建立了索引,也不会再走索引了

  • 执行计划分析

以下是上述SQL的执行计划,已剔除无关部分。 

Plan hash value: 3381251447------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation              | Name                               | Starts | E-Rows |E-Bytes|E-Temp | Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |                                    |      1 |        |       |       | 30538 (100)|          |      1 |00:00:10.03 |   73476 |       |       |          |
|   1 |  SORT AGGREGATE        |                                    |      1 |      1 |    27 |       |            |          |      1 |00:00:10.03 |   73476 |       |       |          |
|*  2 |   HASH JOIN            |                                    |      1 |   5265K|   135M|    67M| 30538   (1)| 00:00:02 |   2498K|00:00:09.59 |   73476 |   148M|    17M|  161M (0)|
|   3 |    INDEX FAST FULL SCAN| UK_20230901210804_1007994          |      1 |   2608K|    37M|       |  2770   (1)| 00:00:01 |   2625K|00:00:00.80 |   11138 |       |       |          |
|*  4 |    INDEX FAST FULL SCAN| IDX_IMO_3                          |      1 |   6647K|    76M|       | 16837   (1)| 00:00:01 |   6162K|00:00:03.13 |   62338 |       |       |          |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------2 - access("LA"."ID"="IMO"."ID")4 - filter(("IMO"."ID" IS NOT NULL AND "IMO"."SOURCE_ID" IS NULL AND "IMO"."STATUS_ID"<>'080' AND "IMO"."STATUS_ID"<>'085'))

id=1(SORT AGGREGATE )

因为上述SQL中有COUNT(),所以 出现了 SORT AGGREGATE 。SORT AGGREGATE并不做真正的排序,不会用到排序空间。所以上述SQL的真正性能问题不在此处。

id=2(HASH JOIN)

两张大表关联,且需要查询大量数据时,需要走hash连接。结合上面的分析,所以此处执行计划,我认为是没有问题的。

id=3 与 id=4

这里大家看不到索引对应的表,这个 UK_20230901210804_1007994 其实是LA表的索引,上文表述过了,LA表的大小小于IMO表,所以理论上LA表应该是hash连接中的驱动表。执行计划中离hash关键字最近的表就是驱动表,所以这里的驱动表没有问题。

且我们可以发现 Used-Mem是161M,说明hash连接消耗了PGA 161M的内存。

根据这个161M需要再进一步和大家分享说明一下hash连接的算法:两表等值关联,返回大量数据,将较小的表作为驱动表。将驱动表的select字段和连接字段读入PGA中,然后对驱动表的连接字段进行hash运算生成hash table;当驱动表的所有数据都读入PGA后,再读取被驱动表,对被驱动表的连接列也进行hash运算;然后在PGA中探测hash table,找到数据就关联上。

所以这就解释了两个大小过G的表只消耗了161M的PGA

除了驱动表与PGA大小,我们可以看到两张表都走了索引。索引扫描是单块读,全表扫描是多块读,读取大量数据时,选择全表扫描更合适,且全表扫描也不会发生回表操作。但因为SELECT 字段只是一个count计数,Oracle的CBO优化器算法可能认为数据量还不够大,且此时也不需要回表,所以走了索引扫描,我认为问题也不是很大。

综上所述,我认为执行计划本身不存在什么太大问题,那怎么优化呢?

  •  调优技巧:并行查询

这里有一个查询调优技巧:开启并行查询。

启用并行查询,说的比较白话一点就是将hash运算拆成n份。

例如对本文SQL启用10个并行查询,LA表会根据连接列进行hash运算然后拆成10份:LA1、LA2、....... LA10;IMO表也会根据连接列进行hash运算然后拆成10份:IMO1、IMO2、..... IMO10。相当于改写成如下SQL:

SELECT 
COUNT(1) AS CNT
FROM LA1 
LEFT JOIN  IMO1 ON LA1.ID = IMO1.ID
WHERE IMO1.SOURCE_ID IS NULL 
AND IMO1.STATUS_ID NOT IN ('080','085')UNION ALLSELECT 
COUNT(1) AS CNT
FROM LA2 
LEFT JOIN  IMO2 ON LA2.ID = IMO2.ID
WHERE IMO2.SOURCE_ID IS NULL 
AND IMO2.STATUS_ID NOT IN ('080','085')......UNION ALLSELECT 
COUNT(1) AS CNT
FROM LA10 
LEFT JOIN  IMO10 ON LA10.ID = IMO10.ID
WHERE IMO10.SOURCE_ID IS NULL 
AND IMO10.STATUS_ID NOT IN ('080','085')

优化方案

那么如何开启并行查询呢?这就需要写HINT,如下代码所示。

SELECT 
/*+ parallel(10) use_hash(LA,IMO) pq_distribute(IMO hash,hash)*/
COUNT(1) AS CNT
FROM LA 
LEFT JOIN  IMO ON LA.ID = IMO.ID
WHERE IMO.SOURCE_ID IS NULL 
AND IMO.STATUS_ID NOT IN ('080','085')

我把HINT单独摘出来,这里添加的hint是pq_distribute(被驱动表 hash hash) ;

其中use_hash(驱动表,被驱动表)的用法是走hash连接,LA是驱动表,IMO是被驱动表,顺序不要错。

/*+ parallel(10) use_hash(LA,IMO) pq_distribute(IMO hash,hash)*/

 此时执行完上述SQL,需要1.5秒左右。速度提升了几倍。

下面是优化后SQL的执行计划

大家可以看到各种资源消耗和ATIME都下降非常多。

SQL_ID  9uwrm2pp44xvp, child number 0
-------------------------------------
SELECT /*+ parallel(10) use_hash(LA,IMO) pq_distribute(IMO hash,hash)*/ 
COUNT(1) CNT FROM INPORD.LAB_APPLY LA LEFT JOIN INPORD.MEDICAL_ORDER 
IMO ON LA.LAB_APPLY_FLOW = IMO.RELATION_KEY WHERE IMO.ORDER_SOURCE_CODE 
IS NULL AND IMO.ORDER_STATUS NOT IN ('080','085')Plan hash value: 4058815446-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                        | Name                               | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   |    TQ  |IN-OUT| PQ Distrib | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                 |                                    |      1 |        |       |  2182 (100)|          |        |      |            |      1 |00:00:00.66 |      24 |       |       |          |
|   1 |  SORT AGGREGATE                  |                                    |      1 |      1 |    27 |            |          |        |      |            |      1 |00:00:00.66 |      24 |       |       |          |
|   2 |   PX COORDINATOR                 |                                    |      1 |        |       |            |          |        |      |            |     10 |00:00:00.66 |      24 | 73728 | 73728 |          |
|   3 |    PX SEND QC (RANDOM)           | :TQ10002                           |      0 |      1 |    27 |            |          |  Q1,02 | P->S | QC (RAND)  |      0 |00:00:00.01 |       0 |       |       |          |
|   4 |     SORT AGGREGATE               |                                    |      0 |      1 |    27 |            |          |  Q1,02 | PCWP |            |      0 |00:00:00.01 |       0 |       |       |          |
|*  5 |      HASH JOIN                   |                                    |      0 |   4917K|   126M|  2182   (1)| 00:00:01 |  Q1,02 | PCWP |            |      0 |00:00:00.01 |       0 |   148M|    17M|   15M (0)|
|   6 |       PX RECEIVE                 |                                    |      0 |   2608K|    37M|   308   (1)| 00:00:01 |  Q1,02 | PCWP |            |      0 |00:00:00.01 |       0 |       |       |          |
|   7 |        PX SEND HYBRID HASH       | :TQ10000                           |      0 |   2608K|    37M|   308   (1)| 00:00:01 |  Q1,00 | P->P | HYBRID HASH|      0 |00:00:00.01 |       0 |       |       |          |
|   8 |         STATISTICS COLLECTOR     |                                    |      0 |        |       |            |          |  Q1,00 | PCWC |            |      0 |00:00:00.01 |       0 |       |       |          |
|   9 |          PX BLOCK ITERATOR       |                                    |      0 |   2608K|    37M|   308   (1)| 00:00:01 |  Q1,00 | PCWC |            |      0 |00:00:00.01 |       0 |       |       |          |
|* 10 |           INDEX FAST FULL SCAN   | UK_20230901210804_1007994          |      0 |   2608K|    37M|   308   (1)| 00:00:01 |  Q1,00 | PCWP |            |      0 |00:00:00.01 |       0 |       |       |          |
|  11 |       PX RECEIVE                 |                                    |      0 |   6207K|    71M|  1871   (1)| 00:00:01 |  Q1,02 | PCWP |            |      0 |00:00:00.01 |       0 |       |       |          |
|  12 |        PX SEND HYBRID HASH (SKEW)| :TQ10001                           |      0 |   6207K|    71M|  1871   (1)| 00:00:01 |  Q1,01 | P->P | HYBRID HASH|      0 |00:00:00.01 |       0 |       |       |          |
|  13 |         PX BLOCK ITERATOR        |                                    |      0 |   6207K|    71M|  1871   (1)| 00:00:01 |  Q1,01 | PCWC |            |      0 |00:00:00.01 |       0 |       |       |          |
|* 14 |          INDEX FAST FULL SCAN    | IDX_IMO_3                          |      0 |   6207K|    71M|  1871   (1)| 00:00:01 |  Q1,01 | PCWP |            |      0 |00:00:00.01 |       0 |       |       |          |
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------5 - access("LA"."ID"="IMO"."ID")10 - access(:Z>=:Z AND :Z<=:Z)14 - access(:Z>=:Z AND :Z<=:Z)filter(("IMO"."ID" IS NOT NULL AND "IMO"."SOURCE_ID" IS NULL AND "IMO"."STATUS_ID"<>'080' AND "IMO"."STATUS_ID"<>'085'))

 

优化总结

/*+ parallel(n) use_hash(LA,IMO) pq_distribute(IMO hash,hash)*/

parallel(n)中的这个n并不是越大就越好。

这种并行查询方法只会在表连接查询消耗PGA大小合适的时候才能发挥最大作用,

它其实对于几十G这种远远超过PGA大小的表连接时,就不好使了;对于这种情况还有其他优化方法。

这个SQL应该还有其他优化方法,毕竟我只优化到了1.5s左右。

但我现在还是太愚笨了,只能想到这个方法优化。

如果后续我找到其他优化方法,会再和大家分享。

谢谢您的阅读!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/830276.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux进入单用户模式指引

文章目录 引言I 通过GRUB进入单用户模式1.1 倒计时界面的操作1.2 GRUB1.3 内核参数编辑界面1.4 更多内核参数编辑界面II 预备知识:Linux用户模式引言 应用场景: root密码重置: 用passwd命令修改root修复登录相关的配置:/etc/pam.d/login 和 /etc/pam.d/sshd 案例:Centos6进…

文件上传复习(upload-labs18-19关)

Pass-18&#xff08;条件竞争&#xff09; 代码和第17关大差不差&#xff0c;所以查看提示 需要用到代码审计 上传图片木马配合解析漏洞进行getshell 新建一句话木马 18.php&#xff0c;代码为&#xff1a; <?php fputs(fopen(../upload/shell18.php,w),<?php phpin…

Unreal Engine添加UGameInstanceSubsystem子类

点击C类文件夹&#xff0c;在右边的区域点击鼠标右键&#xff0c;在弹出的菜单中选择“新建C类”在弹出的菜单中选中“显示所有类”&#xff0c;选择GameInstanceSubsystem作为父类, 点击“下一步”按钮输入子类名称“UVRVIUOnlineGameSubsystem”&#xff0c;选择插件作为新类…

【大数据与云计算】虚拟机安装Linux

前言&#xff1a;使用Linux系统对大数据学习必不可少&#xff0c;本文主要介绍虚拟机安装linux的流程 文章目录 一、 下载VMware二、下载Linux三、安装Linux 一、 下载VMware 官网链接 下载VMware-player&#xff0c;一直下一步安装即可。 二、下载Linux 点击链接直接下载&…

第一个大型汽车ITU-T车载语音通话质量实验室投入使用

中国汽车行业蓬勃发展&#xff0c;尤其是新能源汽车风起云涌&#xff0c;无论是国内还是海外需求旺盛的趋势下&#xff0c;除乘用车等紧凑型车外&#xff0c;中型汽车如MPV、小巴、小型物流车&#xff0c;大型汽车如重卡、泥头车等亦加入了手机互联、智驾的科技行列&#xff0c…

PD虚拟机(Parallels Desktop)2024mac苹果电脑19免费版下载

PD虚拟机&#xff08;Parallels Desktop 虚拟机&#xff09;是一款知名的系统虚拟化软件&#xff0c;PD虚拟机允许用户在一个操作系统中同时运行另一个或者多个操作系统。这种技术在多种场景中非常有用&#xff0c;比如程序开发、专业研究、游戏对战等&#xff0c;尤其是对于需…

Kafka 3.x.x 入门到精通(03)——Kafka基础生产消息

Kafka 3.x.x 入门到精通&#xff08;03&#xff09;——对标尚硅谷Kafka教程 2. Kafka基础2.1 集群部署2.2 集群启动2.3 创建主题2.4 生产消息2.4.1 生产消息的基本步骤2.4.2 生产消息的基本代码2.4.3 发送消息2.4.3.1 拦截器2.4.3.1.1 增加拦截器类2.4.3.1.2 配置拦截器 2.4.3…

LangChain之各个输出解析器的使用

Model I/O 在LangChain中&#xff0c;Model I/O被称为&#xff1a;模型的输入与输出&#xff0c;其有输入提示(Format)、调用模型(Predict)、输出解析(Parse)等三部分组成。 makefile 复制代码 1.提示模板: LangChain的模板允许动态选择输入&#xff0c;根据实际需求调整输入内…

从业务经营到企业战略,构建制药企业数字化应用新能力

我国医药的消费正处在一个高速增长的阶段&#xff0c;人口增长、老龄化加剧、经济总体增长、人均消费增长、农村收入提高&#xff0c;这五大因素是医药市场蓬勃发展的动力。在这五大因素的驱动下&#xff0c;我国的医药市场需求将会在未来相当长的时间内保持高速增长。从多个环…

(三)登录和注册(handle_auto.go)

登录和注册(handle_auto.go) 文章目录 登录和注册(handle_auto.go)一、所需要的结构体信息二、注册三、登录四、退出 一、所需要的结构体信息 type UserAuth struct{}type LoginReq struct {Username string json:"username" binding:"required"Password …

错误代码126:加载xinput1_3.dll失败如何解决?8个解决方法分享

xinput1_3.dll是Windows操作系统中一个非常关键的动态链接库&#xff08;Dynamic Link Library, DLL&#xff09;文件&#xff0c;它是微软DirectX软件开发包的组成部分&#xff0c;专门用于支持游戏控制器和其它输入设备在游戏及多媒体应用程序中的交互。下面是对xinput1_3.dl…

封装 H.264 视频为 FLV 格式然后推流

封装 H.264 视频为 FLV 格式并通过 RTMP 推流 flyfish 协议 RTMP (Real-Time Messaging Protocol) RTSP (Real Time Streaming Protocol) SRT (Secure Reliable Transport) WebRTC RTMP&#xff08;Real Time Messaging Protocol&#xff09;是一种用于实时音视频流传输的协…

qt安装历史版本5.15.2

0 背景 因为需要&#xff0c;所以需要安装qt5的最后一个版本qt5.15.2&#xff0c;但是下载qt安装器后&#xff0c;发现没有想要的版本。后面才发现&#xff0c;可以筛选历史版本进行安装。 1 解决 1&#xff0c;打开qt安装程序&#xff0c;勾选Archive后&#xff0c;点击筛选…

Python实现自动化的服务器部署和配置管理库之pyinfra使用详解

概要 在现代软件开发中,自动化部署和配置管理变得越来越重要。Python pyinfra库是一个强大的工具,可以帮助开发者实现自动化的服务器部署和配置管理。本文将介绍pyinfra库的安装、特性、基本功能、高级功能、实际应用场景以及总结。 安装 首先,来看一下如何安装pyinfra库。…

服务器被CC攻击怎么办

遇到CC攻击时&#xff0c;可采取以下措施&#xff1a;限制IP访问频率、启用防DDoS服务、配置Web应用防火墙、增加服务器带宽、使用负载均衡分散请求压力。 处理服务器遭遇CC攻击的方法如下&#xff1a; 1. 确认攻击 你需要确认服务器是否真的遭受了CC攻击&#xff0c;这可以…

书生浦语训练营第三节笔记和作业-茴香豆 搭建你的Rag智能助理

书生 浦语 茴香豆项目是一个基于大型语言模型&#xff08;LLM&#xff09;的群聊知识助手&#xff0c;由上海人工智能实验室的书生浦语团队开发。这个项目利用了RAG&#xff08;Retrieval-Augmented Generation&#xff09;技术&#xff0c;通过检索与用户输入相关的信息&#…

P6技巧-关于汇总项目Summarize的使用

前言 不知你在使用P6项目时是否察觉到这么一个有趣的现象&#xff0c;但打开一个项目&#xff08;展开详细任务&#xff09;时&#xff0c;在项目页签下可以看到该项目能反馈此时项目的总体进展&#xff0c;完成时间等内容&#xff1b;而当项目关闭时&#xff0c;其前1s所展示…

Linux快速部署大语言模型LLaMa3,Web可视化j交互(Ollama+Open Web UI)

本文在个人博客同步发布&#xff0c;前往阅读 1 介绍 本文将介绍使用开源工具Ollama(60.6k⭐)部署LLaMa大模型&#xff0c;以及使用Open WebUI搭建前端Web交互界面的方法。 我们先来过一遍几个相关的概念&#xff0c;对这块比较熟悉的朋友可跳过。 1.1 大规模语言模型 大规…

Eclipse内存分析器 Java内存分析工具MAT(Memory Analyzer Tool)的介绍与使用

1.visualvm实时监测 2.Memory Analyzer Tool打开 3.工具的使用可以参考 Java内存分析工具MAT(Memory Analyzer Tool)的介绍与使用 ------------------------ 1.我远程发现是其中一个客户端A请求服务器页面响应&#xff0c;一直得不到响应&#xff0c;然后客户端A一直请求&am…

Ansible 自动化运维

一、介绍 1、定义&#xff1a; ansible是自动化运维工具&#xff0c;基于Python开发&#xff0c;具有批量系统配置、批量程序部署、批量运行命令等功能。 ansible是基于 paramiko&#xff08;框架&#xff09; 开发的&#xff0c;并且基于模块化工作&#xff0c;本身没有批量…