经典案例:如何优化Oracle使用DBlink的SQL语句
转自 https://blog.csdn.net/Enmotech/article/details/78788083
作者介绍
赵全文
就职于太极计算机股份有限公司,在中央电化教育馆做Oracle DBA的驻场运维工作。具有3年左右的Oracle工作经验,目前擅长Oracle数据库的SQL脚本编写、故障诊断和性能优化,并且乐于分享Oracle技术。
一般在DBLINK的SQL语句中,将调用远程表的in-line view结果集返回的数据尽量减少,进而达到通过网络传输的数据减少的目的,而且也不会将数据传输的资源消耗在大量的网络等待事件上。在Oracle中这样的等待事件是:SQL*Net message from DBLINK。
正巧,前段时间我们的Oracle生产库正好也碰到了这样的几条类似的SQL。所以,今天向大家分享一下,一次针对Oracle中使用DBLINK的SQL语句的优化思路分析过程。
发现问题
首先从EMCC监控上,发现一条SQL语句执行好长时间没有执行完毕。
问题解析(一)
根据其SQL_ID的值抓取出的SQL完整语句如下所示:
发现上面的SQL语句里有一个绑定变量":1"。
通过视图v$sql_bind_capture(或者dba_hist_sqlbind)进行查看具体的值。
或者也可以用SQLT(全称SQLTXPLAIN,关于SQLT的下载、安装和使用,请看Oracle MOS 215187.1)生成分析SQL_ID为83gn36c1fu9dw的报告,从报告中找出绑定变量”:1”具体的值(可能有很多),由于我的数据库服务器上已经部署了SQLT,生成报告的过程这里略过,查看绑定变量值的过程如下图所示。
然后,将上面查出的值”ff80808141c605e20141c9691f5a000c”带入原始的SQL语句并在SQL*Plus里执行,运行5分26秒才显示查询结果。可想而知,在当前的高并发情况下,这样的一条SQL语句花很长时间执行不完也就不足为奇了,整个过程如下图所示。
分析整个SQL语句的结构
其中最外层的SELECT是一个ROWNUM操作,也就是取内层结果集并返回前5行;
再往里的一层完全可以去掉,(这个我经过测试是可行的);
再往里看的一层就是内联视图r (查询远程表sd_res_id_case返回的结果集)与本地表t进行左联接;
最终返回整个查询结果。
大家仔细看一下那个内联视图r,你会发现里面还有一个子查询(就是and rowid in下面的那层)。
生成带统计信息的执行计划,如下图所示:
看第3步的NESTED LOOPS,Starts*E-Rows=1*2=2,而A-Rows=926K,我们说Starts*E-Rows的值和A-Rows的值应该相等或者相差不多,再看第8行的REMOTE,Starts*E-Rows=926K*3,A-Rows=5,这两个值也相差很大。而且这个REMOTE的Starts是926K,这说明要执行这么多次,这个太消耗资源而且还是在远程库的表上。
接下来,在执行计划后面的”Remote SQL Information”中可以看出有两个REMOTE操作,也就是说这条SQL语句的内联视图r并不是整体从远程表上查询出结果再返回到本地库,而是先执行第5步,再执行第8步,总共查询了两次远程表。
那么试想一下看能不能让远程表只查询一次,也就是让内联视图r只执行一次就返回远程表sd_res_id_case的查询结果?
结果当然是可以的,用一个no_merge的Hint放在内联视图r的第一个select 之后,更改之后是下面这样的:
竟然发现大约7秒就查询出结果,如下图所示,
接着,查看附加统计信息的执行计划。
最主要的是,执行时间大大降低,而且在执行计划里只有一个REMOTE操作,第二步变成了HASH JOIN操作(原先的执行计划是NESTED LOOPS),估计行和返回行都是5。
接下来再看第5行的VIEW操作,执行1次,估行行为5754,实际行为66165,这个相差10倍左右,估计还有优化的空间。
远程库上查看内联视图r的数据量
由于远程表的执行计划在本地库上无法查看,那么我们到远程库上查询一下原SQL语句的内联视图r,看看到底有多少数据。
在远程库上做如下操作。
竟然返回196372(约196K)行,这个值高的超乎我想象。
查看带统计信息的执行计划,如下图所示,
第2行的”NESTED LOOPS”操作实际返回行196K,也就是SQL语句中的最外层select count(*)操作;
第7行的”TABLE ACCESS BY USER ROWID”操作也是实际返回行196K(仔细看,Starts的值为196K,也就是执行196K次,这个好恐怖),第7行的操作就是子查询”select min(rowid) from ……”。
这样看来SQL语句的外层select有多少行,里面的子查询就执行多少次,而现在的外层select是196K行,然后呢,196K*196K = ?我都不敢想……
总体上看,加一个no_merge的Hint,先是让SQL的执行时间与原先相比降低了好多。
于是,我和开发同事进行沟通,我才明白SQL是应用服务器里跑的一个定时任务,每天凌晨4点开始执行,最后他给程序里的SQL增加no_merge的Hint。
问题解析(二)
第二天,我用视图v$active_session_history查看凌晨4点到6点的DBLINK等待事件。
从上面的查询,我们可以看出,有两条SQL的DBLINK等待事件总数多的离谱。其实另外一条SQL和我前面分析的那条唯一的区别就是在select最外层又加了一个ROWNUM <= ":2" 的条件,目前我们只分析原先的那条。
那么,再查询6点到7点的情况,已经没有DBLINK的等待事件,说明那些相关的SQL执行完毕,如下图所示。
另外,我们从AWR的对比报告中也可以看出上面的查询结果(AWR是从视图DBA_HIST_ACTIVE_SESS_HISTORY中读取相关信息)。
从上面的AWR图中我们还可以看出那两条SQL的执行次数分别为3106和3039。
从前面的执行计划分析,我们了解到SQL主要慢在内联视图r的返回行很多,那么继续优化就是要改写内联视图。
首先,将内联视图r的外层select查询中增加和内层select查询中同样的where条件,这样就能过滤掉许多行,同时将两层select查询中的school_id字段进行关联,如下图所示。
然而只需4毫秒就显示查询结果,带统计信息的执行计划如下图所示,
接下来,我和开发同事进行了沟通并把我改写后的SQL发给他,他测试运行和原先SQL相比,也认为在运行时间上差了一个数量级。后来,他根据业务的需求改写了原来的SQL,整个改写后的SQL语句如下图所示。
查看带统计信息的执行计划,如下图所示。
通过上面的执行计划,大家可以看出Starts、E-Rows、A-Rows的值都变得很小了,A-Time的值为1~2毫秒。
第三天,再次查看相应时间段的DBLINK等待事件总数,发现与原来相比已经降低了很多。
再查看SQL_ID为a50rh3659p44q的SQL在相应对间段的执行次数,见下图。
同样的,从下面折AWR报告中也能看出和上面的查询一样的效果。
总结
最后对使用DBLINK的SQL优化过程总结:
(1) 从EMCC监控上抓取有问题的SQL;
(2) 通过给SQL增加gather_plan_statistics的Hint通过实际运行测试;
(3) 生成相应的行源执行计划并分析哪一步操作最消耗时间;
(4) 找出对应的方法(并不一定是改写,这个根据具体情况而定),再次进行测试;
(5) 与开发人员沟通,并重新审核修改SQL代码。(若无需更改代码的优化,那就再好不过了)
相关文献参考:
https://community.oracle.com/thread/4083373
https://community.oracle.com/thread/4100882
特别鸣谢(排名不分先后):
Jonathan Lewis,
Andrew Sayer,
Billy~Verreynne,
BEDE,
Manik,
perfdba,
Paulzip,
Mustafa KALAYCI,
Cookiemonster76,
Sven W.,
padders
等国外Oracle的技术专家们,是他们在Oracle Developer Community(https://community.oracle.com/welcome)上针对我的发贴提问给予了细心的指导。
经典案例:如何优化Oracle使用DBlink的SQL语句的更多相关文章
- Oracle数据库常用的Sql语句整理
Oracle数据库常用的Sql语句整理 查看当前用户的缺省表空间 : select username,default_tablespace from user_users; 2.查看用户下所有的表 : ...
- oracle 修改表的sql语句
oracle 修改表的sql语句 1增加一个列:ALTER TABLE 表名 ADD(列名 数据类型);如:ALTER TABLE emp ADD(license varchar2(256)) ...
- 查询Oracle正在执行的sql语句
--查询Oracle正在执行的sql语句及执行该语句的用户 SELECT b.sid oracleID, b.username 登录Oracle用户名, b.serial#, spid 操作系统ID, ...
- oracle 监控执行的sql语句
oracle 监控执行的sql语句 select * from v$sqlarea a where module='PL/SQL Developer' order by a.FIRST_LOAD_TI ...
- oracle数据库查询日期sql语句(范例)、向已经建好的表格中添加一列属性并向该列添加数值、删除某一列的数据(一整列)
先列上我的数据库表格: c_date(Date格式) date_type(String格式) 2011-01-01 0 2012-03-07 ...
- Oracle中,利用sql语句中的函数实现保留两位小数和四舍五入保留两位小数
Oracle中,利用sql语句中的函数实现保留两位小数和四舍五入保留两位小数: select trunc(1.23856789,2) from dual round(m,n) 可以四舍五入 trunc ...
- 查询Oracle正在执行的sql语句及kill被锁的表
查询Oracle正在执行的sql语句及执行该语句的用户SELECT b.sid oracleID, b.username 登录Oracle用户名, b.serial#, spid 操作系统ID, pa ...
- Oracle 的分页查询 SQL 语句
Oracle的分页查询语句基本上可以按照本文给出的格式来进行套用. 分页查询格式: SELECT * FROM (SELECT A.*, ROWNUM RN FROM (SELECT * FROM T ...
- 优化数据库的方法及SQL语句优化的原则
优化数据库的方法: 1.关键字段建立索引. 2.使用存储过程,它使SQL变得更加灵活和高效. 3.备份数据库和清除垃圾数据. 4.SQL语句语法的优化.(可以用Sybase的SQL Expert,可惜 ...
随机推荐
- [TimLinux] Python C3 MRO
MRO:Method Resolution Order,即方法解析顺序,是python中用于处理二义性问题的算法 采用过的算法: 1. DFS(深度优先算法) 2. BFS(广度优先算法) 3. C3 ...
- django学习01-建project和app
按装完成后,执行命令 django-admin startproject mysite 建一个名字为mysite的工程.运行 python manage.py runserver 启动网络服务器,然后 ...
- 利用Bootstrap搭建网站页面
先来看下页面效果 <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...
- 【JS】382- JavaScript 模块化方案总结
本文包含两部分,第一部分通过简明的描述介绍什么是 CommonJS.AMD.CMD.UMD.ES Module 以及它们的常见用法,第二部分则根据实际问题指出在正常的 webpack 构建过程中该如何 ...
- Java设计模式的7种设计原则还有很多人不知道
前言 其实没有设计模式我们也能完成开发工作.但是为什么需要设计模式呢?让你看起来很牛,没错这个算一个.让你的代码层次感分明,可读性强而且容易维护.让你像我一样有更多的摸鱼划水时间. 可能有人说我一个类 ...
- django----cookie与session 和 中间件
目录 cookie与session简介及操作 cookie django中操作cookie cookie超时时间 删除cookie session session操作 设置 key value发生了什 ...
- 更新Preloader和uboot
在SoCEDS环境下编译和更新preloader和uboot程序的方法 前面有介绍preloader在HPS boot过程中的的作用,接下来讲述下用户在SoCEDS环境下改如何编译preloade ...
- Redis Cluster 的数据分片机制
上一篇<分布式数据缓存中的一致性哈希算法> 文章中讲述了一致性哈希算法的基本原理和实现,今天就以 Redis Cluster 为例,详细讲解一下分布式数据缓存中的数据分片,上线下线时数据迁 ...
- Python基础-day01-8
变量的基本使用 程序就是用来处理数据的,而变量就是用来存储数据的 目标 变量定义 变量的类型 变量的命名 01. 变量定义 在 Python 中,每个变量 在使用前都必须赋值,变量 赋值以后 该变量 ...
- 如何快速将百度大脑AI技术内置智能小程序中
实现效果: 该AI智能小程序目前集成了百度AI开放平台数十个AI服务产品功能,包括人脸识别.文字识别.表格识别.红酒识别.货币识别.地标识别.手势识别.商标识别.果蔬识别.菜品识别等图片识别功能,以及 ...