记一次 Oracle 下的 SQL 优化过程
1. 介绍
事情是这样的,UAT 环境的测试小伙伴向我扔来一个小 bug,说是一个放大镜的查询很慢,转几分钟才出数据,我立马上开发环境试了一下,很快啊我说,放大镜的数据立马就出来了,然后我登录 UAT 环境一看,诶是有些慢 ,于是开始了我的排查之旅...
2. 过程
首先我立马拿到了执行的 SQL 在开发环境的数据库执行了下,很快,都在 1s 左右,感觉没啥问题啊,然后我就在页面上点点点,发现好像上面有一个相关联的下拉框,如果选中的有数据,再点击这个放大镜就会慢一点,然后我登录 UAT 环境一试,哦不是这个问题,于是只能开始排查 SQL 了。
百度了一圈 Oracle 性能调优,大多很空泛,没有一个通用的、具体的、可执行的步骤。但是找到了排查前必备的查看执行计划explain plan。
以下是正儿八经的优化过程:
2.1 查看该条 SQL 的执行计划
2.1.1 生成执行计划
在要排查的SQL前面加上explain plan for,例如以下的例子:
explain plan for
SELECT
*
FROM
SOURCE_LISTEX_202101
WHERE
pass_id = '012101200123001025061320201201002852';
2.1.2 查看执行计划
推荐使用该 SQL 去查询执行计划 (为什么?因为简短好记)
select * from table(dbms_xplan.display)
或者
SELECT plan_table_output FROM TABLE(DBMS_XPLAN.DISPLAY('PLAN_TABLE'));
查出来的应该是这个样子:
Plan hash value: 1335523602
-----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 44479 | 38M| 17411 (1)| 00:03:29 |
| 1 | TABLE ACCESS BY INDEX ROWID | SOURCE_LISTEX_202101 | 44479 | 38M| 17411 (1)| 00:03:29 |
|* 2 | INDEX RANGE SCAN | LISTEX_202101_PASS_ID | 17792 | | 3 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("PASS_ID"='012101200123001025061320201201002852')
而且如果你的 SQL 长了之后会发现,Operation 列是会有缩进的,缩进代表层级关系,就很乱,这里我推荐 Datagrip的右键可视化Explain plan。
使用方法为:选中SQL,右键Explain Plan,就可以查看啦,大概长这个样子:
其中点击 Explain Plan (Raw),也是可以的,就是查看原生的执行计划样子,大概长这样:
2.1.3 分析执行计划
在 1.2 节可视化那个图中,我们主要看表格中的 Total Cost, 它代表着该条操作的总消耗,我们根据层级关系逐个排查,找到最为耗时的操作,排查发现此处两个Full Scan 全表扫描的性能消耗占据了全部总消耗的98% ((384+384)/779)
之后根据执行计划的层级关系我们去 SQL 中找到这两个全表扫描对应的部分:

查一下 sett_ebankinstruct 这个表的数量 有近十万条,看了一下表的定义,一个索引都没加。。。
在此次 SQL 中,使用sett_ebankinstruct的字段只有 instructionid 和 instructtype。
instructionid : 很有可能会作为关联条件去连接多个表,并且该字段不会频繁的update,故在该字段上加索引。
如何加索引呢?
create index 索引名称
on 表名 (字段);
在此处加索引的 SQL 为:
create index IDX_SETT_EBANKINSTRUCT_INSTRID
on SETT_EBANKINSTRUCT (INSTRUCTIONID);
instructtype : 考虑到 instructtype 只是类型,并且使用的情况可能就是 = 或者in (具体的几个值),于是就不用加。
我们添加索引之后发现,第一个Full Scan 全表扫描已经消失,因为在 sp.id = se.instructionid 进行表连接的时候走了索引,但是第二个Full Scan全表扫描仍然存在,说明此处并未走索引 :

原因是此处使用 not in ,括号里的instructionId并未走内部子查询的索引,那么怎么改成走索引呢?将其改写成 not exists 即可。

再次查看下执行计划,发现两个全表扫描都消失了,都变成了索引扫描(Index Scan):

对比优化前后Total Cost:
优化前:779
优化后:62
优化提升:92%
3. 结论
如果排查确实是 SQL 问题,就直接看 执行计划 ,重点关注占用Total Cost的部分,然后查看对应的 SQL 。
- 如果是表频繁连接的字段,就要考虑加索引了。
not in改not exists,(业界流传not exists比not in快)
其实非也,如果主查询和子查询表大小相当,那么用in和exists差别不大。
如果子查询表大,用exists快,如果子查询表小,用in快。Where:数据量多的情况下,排除越多记录的条件应该是先执行。
Oracle下能排除掉多的条件放后面,因为Oracle的where是从右往左执行的,格式化 SQL 后也就是从下往上执行,这样写那么会先排除大量的数据,因而加快后续操作的速度。
MySQL正好和Oracle相反,MySQL下的Where是从左往右执行的,格式化 SQL 之后也就是从上往下执行,
因而MySQL下Where的条件应该是排除多的条件放前面。
记一次 Oracle 下的 SQL 优化过程的更多相关文章
- SQL优化过程中常见Oracle HINT
在SQL语句优化过程中,我们经常会用到hint,现总结一下在SQL优化过程中常见Oracle HINT的用法: 1. /*+ALL_ROWS*/ 表明对语句块选择基于开销的优化方法,并获得最佳吞吐量, ...
- oracle中的SQL优化
一.SQL语言的使用1.IN 操作符 用IN写出来的SQL的优点是比较容易写及清晰易懂,这比较适合现代软件开发的风格. 但是用IN的SQL性能总是比较低的,从ORACLE执行的步骤来分析用 ...
- Oracle ->> Oracle下实现SQL Server的TOP + APPLY
今晚很好奇想知道Oracle下有没有APPLY子句?如果有那怎么实现SQL Server下的TOP + APPLY.结果自己写了个例子. with a as ( order by grp_factor ...
- centos下美团sql优化工具SQLAdvisor的安装
1.克隆代码 cd /usr/local/src/git clone https://github.com/Meituan-Dianping/SQLAdvisor.git 2.安装依赖(ubuntu下 ...
- 记一次Sql优化过程
这几天在写一个存储过程,反复优化了几次,从最开始的7分钟左右,优化到最后的几秒,并且这个过程中我的导师帮我指点了很多问题,这些指点都是非常宝贵的,独乐乐不如众乐乐,一起来分享这次的优化过程吧. 这个存 ...
- 记一次Entity Framework 项目的优化过程
在博客园看了不少其他大神的经验.今天也抽空贡献点自己的经验(并不是说自己也是大神..小弟还只新手程序员去年才毕业的) 好了废话不多说,直接进入主题.(具体的好坏各位看官就随便看看吧..没有什么好坏之分 ...
- oracle存储过程及sql优化-(一)
本篇主要介绍存储过程的结构 先简单介绍下: oracle存储过程与函数不同,oracle函数和存储过程都可以有多个输入,但是函数一般只有一个输出,而oracle可以有多个输出且与输入 ...
- oracle存储过程及sql优化-(三)
接下来介绍上篇接触到的存储过程中的sql语句 insert into TMP_GT3_sbfgl_WJSTJB SELECT NSR.NSRSBH, NSR.NSRMC, NSR.SCJYDZ, ca ...
- oracle存储过程及sql优化-(二)
接下来比较重要,我会先贴出一个存储过程,根据这个存储过程讲解 PROCEDURE AP_CXBB_GT3_SBFGL_SBFYJSQC (OUT_RECORD OUT SYS_REFCURSOR, P ...
- 记一次Oracle冷备恢复的过程
一.故障来临 某日中午,市电意外中断,机房UPS电源由于负载过重而未接管供电,所有服务器全部重启...... 待所有服务器重启后,正在逐一检查设备和业务运行情况时,意外发生了.一台年代久远的HP PC ...
随机推荐
- MongoRepository查询数据常用语法
安装 查看mongo可用版本 docker search mongo 安装指定版本的mogo或者拉取最新版本的镜像 docker pull mongo:latest 或者官网下载安装包 https:/ ...
- [极客大挑战 2019]BuyFlag 1
好吧,又是一道违背我思想的题目,哦不哦不不对,是本人操作太傻了 首先进入主页面 没有发现什么奇怪的东西,查看源代码,搜索.php 可以看到有一个pay.php,访问查看 给我们了一些提示 FLAG N ...
- 痞子衡嵌入式:MCUXpresso IDE下生成镜像文件的方法及其与IAR,MDK差异
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是MCUXpresso IDE下生成镜像文件的方法及其与IAR,MDK差异. 痞子衡很久以前写过一篇文章 <ARM Cortex-M ...
- 推荐一套轻量级的开源图床系统:Light Fast Picture
如果您跟我一样平时有些博客的习惯,那么图片存储是否有困扰过你呢?今天就给大家推荐一款不错的开源图床系统:Light Fast Picture 它是一个基于koa + vue3.x + typescri ...
- 怎么在php7项目中安装event扩展
今天就跟大家聊聊有关怎么在php7项目中安装event扩展,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获. 安装支持库libevent wget ...
- $\mathcal{2023WinterHoliday}$刷题总结
\(\mathcal{2023WinterHoliday}\) \(\mathcal{CTF}\) \(\mathcal{web}\) 1.\(json格式:\)$json['x']=="w ...
- 谁能真正替代你?AI辅助编码工具深度对比(chatGPT/Copilot/Cursor/New Bing)
写在开头 这几个月AI相关新闻的火爆程度大家都已经看见了,作为一个被裹挟在AI时代浪潮中的程序员,在这几个月里我也是异常兴奋和焦虑.甚至都兴奋的不想拖更了.不仅仅兴奋于AI对于我们生产力的全面提升,也 ...
- Thread 线程中的 Synchronized block and lock
Thread Definition of Synchronized Synchronized block in java are marked with the synchronized keywor ...
- ubuntu容器的远程xface桌面环境搭建
一.container: ubuntu20.04 二.commands: apt install xfce4 tigervnc-standalone-server # xface使用gdm3启动器 ...
- 卸载wamp忘记备份MySql,如何恢复MySql数据
大家把wamp卸载了,但是数据库忘记备份了.怎么办?不要急,不要慌!打开wamp所在目录(前提是你没有删),你会发现wamp特别良心的帮你把MySql的data文件夹留下来了,这个时候你只要把这个文件 ...