Classic case: how to optimize Oracle SQL statement using DBlink

Transfer from https://blog.csdn.net/Enmotech/article/details/78788083

about the author

Zhao Quanwen

He worked Taiji Computer Co., Ltd., in the central hall audio-visual education to do Oracle DBA residencies operation and maintenance work. Oracle has about three years of work experience, specializes in Oracle database is currently writing SQL scripts, troubleshooting and performance optimization, and willing to share Oracle technology.

In general DBLINK of SQL statements, calls the remote table in-line view the result set returned to minimize the data, thus achieving reduction through data network transmission purposes, but not for data transmission will consume a large amount of network resources wait on events. In Oracle wait events is such: SQL * Net from the Message DBLINK .

It just so happened some time ago our production Oracle database have encountered just such a few similar SQL. So, today, to share with you, the first use of the optimization ideas DBLINK analysis of SQL statements against Oracle.

problem found

First, from the EMCC monitor, we found a SQL statement is executed for a long time has not been completed.

 

640?wx_fmt=png&wxfrom=5&wx_lazy=1

 

Problem Analysis (a)

The value SQL_ID crawl out full SQL statement is as follows:

 

0?wx_fmt=png

 

Find the above SQL statement in a bind variable ": 1."

View through the view specific value of v $ sql_bind_capture (or dba_hist_sqlbind).

0?wx_fmt=png

 

Or you can use SQLT (full name SQLTXPLAIN, download on SQLT, installation and use, see Oracle MOS 215187.1) to generate the analysis SQL_ID 83gn36c1fu9dw report, to find out from the report that bind variables ": 1," a specific value (may have lot), due on my database server has been deployed SQLT, the process of generating the report here skip the process to see bind variable values as shown below.

 

0?wx_fmt=png

0?wx_fmt=png

0?wx_fmt=png

 

然后,将上面查出的值”ff80808141c605e20141c9691f5a000c”带入原始的SQL语句并在SQL*Plus里执行,运行5分26秒才显示查询结果。可想而知,在当前的高并发情况下,这样的一条SQL语句花很长时间执行不完也就不足为奇了,整个过程如下图所示。

 

0?wx_fmt=png

 

分析整个SQL语句的结构

 

其中最外层的SELECT是一个ROWNUM操作,也就是取内层结果集并返回前5行;

再往里的一层完全可以去掉,(这个我经过测试是可行的);

再往里看的一层就是内联视图r (查询远程表sd_res_id_case返回的结果集)与本地表t进行左联接;

最终返回整个查询结果。

大家仔细看一下那个内联视图r,你会发现里面还有一个子查询(就是and rowid in下面的那层)。

生成带统计信息的执行计划,如下图所示:

 

0?wx_fmt=png

0?wx_fmt=png

 

看第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 之后,更改之后是下面这样的:

 

0?wx_fmt=png

 

竟然发现大约7秒就查询出结果,如下图所示,

 

0?wx_fmt=png

 

接着,查看附加统计信息的执行计划。

 

0?wx_fmt=png

0?wx_fmt=png

 

最主要的是,执行时间大大降低,而且在执行计划里只有一个REMOTE操作,第二步变成了HASH JOIN操作(原先的执行计划是NESTED LOOPS),估计行和返回行都是5。

 

接下来再看第5行的VIEW操作,执行1次,估行行为5754,实际行为66165,这个相差10倍左右,估计还有优化的空间。

 

远程库上查看内联视图r的数据量

 

由于远程表的执行计划在本地库上无法查看,那么我们到远程库上查询一下原SQL语句的内联视图r,看看到底有多少数据。

 

在远程库上做如下操作。

 

0?wx_fmt=png

 

竟然返回196372(约196K)行,这个值高的超乎我想象。

 

查看带统计信息的执行计划,如下图所示,

 

0?wx_fmt=png

 

第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等待事件。

 

0?wx_fmt=png

0?wx_fmt=png

 

从上面的查询,我们可以看出,有两条SQL的DBLINK等待事件总数多的离谱。其实另外一条SQL和我前面分析的那条唯一的区别就是在select最外层又加了一个ROWNUM <= ":2" 的条件,目前我们只分析原先的那条。

 

那么,再查询6点到7点的情况,已经没有DBLINK的等待事件,说明那些相关的SQL执行完毕,如下图所示。

 

0?wx_fmt=png

 

另外,我们从AWR的对比报告中也可以看出上面的查询结果(AWR是从视图DBA_HIST_ACTIVE_SESS_HISTORY中读取相关信息)。

 

0?wx_fmt=png

0?wx_fmt=png

0?wx_fmt=png

 

从上面的AWR图中我们还可以看出那两条SQL的执行次数分别为3106和3039。

 

从前面的执行计划分析,我们了解到SQL主要慢在内联视图r的返回行很多,那么继续优化就是要改写内联视图。

 

首先,将内联视图r的外层select查询中增加和内层select查询中同样的where条件,这样就能过滤掉许多行,同时将两层select查询中的school_id字段进行关联,如下图所示。

 

0?wx_fmt=png

 

然而只需4毫秒就显示查询结果,带统计信息的执行计划如下图所示,

 

 

0?wx_fmt=png

 

接下来,我和开发同事进行了沟通并把我改写后的SQL发给他,他测试运行和原先SQL相比,也认为在运行时间上差了一个数量级。后来,他根据业务的需求改写了原来的SQL,整个改写后的SQL语句如下图所示。

 

0?wx_fmt=png

 

查看带统计信息的执行计划,如下图所示。

 

 

0?wx_fmt=png

 

通过上面的执行计划,大家可以看出Starts、E-Rows、A-Rows的值都变得很小了,A-Time的值为1~2毫秒。

 

第三天,再次查看相应时间段的DBLINK等待事件总数,发现与原来相比已经降低了很多。

 

0?wx_fmt=png

 

再查看SQL_ID为a50rh3659p44q的SQL在相应对间段的执行次数,见下图。

 

 

0?wx_fmt=png

 

同样的,从下面折AWR报告中也能看出和上面的查询一样的效果。

 

0?wx_fmt=png

0?wx_fmt=png

0?wx_fmt=png

总结

最后对使用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

 

Special thanks to (in alphabetical order):

Jonathan Lewis,

Andrew Sayer,

Billy-Verreynne,

I WILL,

Manik,

perfdba,

Paulzip,

Mustafa KALAYCI,

Cookiemonster76,

Sven W.,

amphibians

Oracle and other foreign technical experts, they gave careful instructions for posting my question on Oracle Developer Community (https://community.oracle.com/welcome).

Guess you like

Origin www.cnblogs.com/zylong-sys/p/12034551.html