您的位置:首页 > 运维架构

10 嵌套循环(NESTED LOOP)--优化主题系列

2017-09-30 09:17 316 查看
Oracle从较小结果集(驱动表/外部表)中读取一行,然后和较大结果集(被探查表/内部表)中的所有数据逐条进行比较(嵌套循环可以用于非等值连接),如果符合规则,就放入结果集中,然后取较小结果集的下一条数据继续进行循环,直到结束。嵌套循环只适合输出少量结果集或者是用于快速输出结果集。

 

conn scott/tiger

alter session set statistics_level=all;

select e.ename,e.job,d.dname from emp e,dept d  wheree.deptno=d.deptno and e.sal<2000;

select * fromtable(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));



NESTED LOOPS表示嵌套循环,Starts表示某个操作执行的次数,那么这里嵌套循环操作一共执行了1次,EMP表离NESTEDLOOPS最近,表示EMP表作为驱动表,它执行了1次(全表扫描)返回了8条记录。Oracle这时候就会从这8条记录中读取一行,然后去匹配内部表,因为驱动表返回了8条记录,所以内部循环会执行8次。

 

提问:嵌套循环的执行计划中那个表是驱动表??驱动表返回10W条数据,被驱动表要扫描10W次对吧??

被驱动表走索引至少读多少个块?假设索引高度是3
即(3+1)*10W=40W是不是要进行 40W
次物理I/O

 

提问:为什么嵌套循环驱动表必须是返回结果集很少??

SQL优化的核心思想是什么??较少I/O扫描的次数对吧

 

提问:为什么嵌套循环被驱动表上面一定要有索引??

假设驱动表返回5000条数据要是被驱动表没索引假设被驱动表有1GB大小是不是5000次的全表扫描??扫描5T??

 

提问:执行计划中一大串嵌套循环套嵌套循环怎么办??

是不是要看最里面的嵌套循环走错了 I/O扛不住的

比如说驱动表返回10W条结果你敢走NL吗??是不是要进行10W次I/O扫描??

 

提问:当看到一个执行计划中有嵌套循环首先应该检查哪里??

直接 SELECTCOUNT(*) FROM
驱动表where
过滤条件.....

你去查询发现 NL
的驱动表返回 10W


但是执行计划的ROWS
只返回 500行

统计信息有问题

假设统计信息没问题那么HINTUSE_HASH

 

提问:驱动表有2GB大小适合做驱动表吗??

驱动表大不大并不关心关心的是返
4000
回行数

如果返回行数太大是不是走NL就错了??应该走HASH连接对吧

 

假设有一个表100W条数据它作为驱动表返回20W条数据那么它的执行计划是对的吗??

假设驱动表返回1条数据被驱动表返回10W条数据最终返回10W条数据对不对??

被驱动表走索引并且返回10W条结果会快吗??

假设索引块一个块存储100条记录要读10W/100=1000次

 

提问:被驱动表的索引建立在哪个列上面?被驱动表上面有过滤条件where
条件在前面还是 JOIN
条件在前面?

嵌套循环里面被驱动表千万不能全表扫描一定要走索引而且索引要建立在join列上

如果是组合索引那么join列不要作为引导列

被驱动表的索引列选择性要高这样聚簇因子影响有限

join列最好是主键如果join列数据不均衡而且聚簇因子很高走索引很坑

 

嵌套循环无需等到所有操作执行完毕才返回结果,它只需要循环匹配上就可以返回结果。如果某个查询返回的结果小于等于驱动表的10%,那么Oracle很有可能选择嵌套循环来处理数据。在嵌套循环中,驱动表/外部表一般很小(这样利用全表扫描),或者是表比较大,然后利用高度可选的索引过滤大量数据,得到结果集(这个结果集一般小于源表10%)来作为驱动表,被驱动表/内部表通常较大,而且在连接列上面建立唯一索引/或者高度可选的非唯一索引(如果被驱动表/内部表上面缺乏索引,CBO通常会选择哈希连接)。

 

为什么要用小表/结果集较小的表作为驱动表??

因为如果表/结果集很大,那么返回的数据就多了,这样循环次数就会增加,会导致多次indexunique/range scan,那么效率就下去了,这个时候CBO会考虑交换驱动表或者干脆选择哈希连接。

 

为什么内部表的连接列上面要有索引??

因为如果内部表很大,如果连接列上面没有索引,那么会导致Oracle对内部表进行全表扫描,而且是多次(根据外部表返回记录数),这样效率极其低下,所以如果内部表的连接列上面没有索引,CBO会选择哈希连接。

 

如何识别错误的nestedloop??

1. 可以通过比较E-Rows和A-Rows,如果两者相差太大,说明执行计划出问题了。这种方法适合OLTP系统。

2. 如果是OLAP,可以人工计算驱动表返回行数,然后比较CBO计算的返回行数,如果两者相差太大,说明执行计划出了问题。

3. 除了查看驱动表之外,我们还要查看被驱动表。被驱动表上面的连接列上通常有唯一索引或者高度可选的非唯一性索引,如果你看到被驱动表走了bitmapindex,那么你要注意了。另外被驱动表也可能是走的fulltable
scan,这个时候我们要selectcount(*)然后比较CBO计算的值,如果2者相差太大,说明执行计划有问题,当然也说明统计信息有问题。

 

 

一般执行计划的 ROWS
要算少

ID=49 返回多少行?

它是不是回收站?

HASH JOIN 之后是不是作为NL
驱动表

返回 354K
条数据

X$KTFBUE (ind:1)

那么这个要被扫描 354K
这么多次

ID=80 这里也有回收站

78 这里又是 NL

79 的HASH JOIN
结果集又作为驱动表

是不是有些基表要被扫描100W次?

ID=47 是不是NL???

48 是不是作为NL
的驱动表

48 是不是返回30多W行?

51 是不是与 HASHJOIN
平级?

ID=51 是不是要被扫描30W次?

执行计划里面估算一般算少

那么是不是数据字典要被扫描上百万次???

如果某个块连续的被扫描上百万次

是不是热点块?

latch: cache buffers chains

现在搞懂了没

系统的热点块怎么产生的?

 

一个系统中热点块怎么产生的??

99% 都是SQL
性能问题而且 99%
都是错误的 NL
引起的

某个块要被一个latch保护

这个 latch
有可能保护另外的块

那么其他SESSION
要来访问这个latch
保护的另外的块是不是又遇到竞争了?

一般一个 latch
只管几十个到几百个块

 

某个系统db filesequential read
排名TOP 1
这个系统一般是什么问题?

比如说某个系统有大批量的 DML
如果没禁止索引那么上面的等待事件也会排名第一

 

用一句话来总结驱动表返回1行被驱动表扫描一次

SQL 优化的核心思想就是减少物理IO
扫描次数

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息