【云和恩墨大讲堂】从执行计划洞察ORACLE优化器的“小聪明”

作者简介

黄浩  惠普

十年一剑,十年磨砺。3年通信行业,写就近3万条SQL;5年制造行业,遨游在ETL的浪潮;2年性能优化,厚积薄发自成一家

主题介绍:

Oracle执行计划的另类解读:调皮的执行计划 | 诚实的执行计划 | 朴实的执行计划

说到执行计划,oracle的拥趸们自然而然会兴奋起来。在ORACLE的世界里,执行计划有着其特殊的地位,如果我们将SQL性能优化看成一个生物,那某种程度上,执行计划就是DNA。在某搜索网站中,“oracle 执行计划”关键字的搜索结果与“oracle”关键字的搜索结果占比为1.7%。足可见执行计划在ORACLE中举足轻重的地位:

而当我们输入“oracle执行计划”时,推荐关键字排第一的就是“ORACLE执行计划怎么看”

一个标准的执行计划大致可以分为三个部分:访问方式(表访问、索引访问等)、连接方式(NESTED LOOP、HASH JOIN等)及访问顺序(驱动表等)

我们对上述SQL稍加改动,再看执行计划:

什么情况?DEPT表不见了,执行计划居然“残缺”了:

1、这是ORACLE的BUG吗?

2、少了一张表,结果正确吗?

3、ORACLE优化器如此大胆,其背后是谁在给他撑腰?

4、ORACLE凭什么擅作主张?

为了回答上述问题,我们就进入今天的第一个主题:残缺的执行计划。

残缺的执行计划

在展开之前,我们先做数据准备,分别创建两张表EMP及DEPT,脚本如下:

 

CREATE TABLE DEPT(

        DEPTNO NUMBER(2), 

        DNAME VARCHAR2(14), 

        LOC VARCHAR2(13)); 

 

CREATE TABLE EMP(

        EMPNO NUMBER(4)CONSTRAINT PK_EMP PRIMARYKEY, 

        ENAME VARCHAR2(10), 

        JOB VARCHAR2(9), 

        MGR NUMBER(4), 

        HIREDATE DATE, 

        SAL NUMBER(7,2), 

        COMM NUMBER(7,2), 

        DEPTNO NUMBER(2) ); 

现在我们有如下一条语句:

SELECT COUNT(1)

  FROM EMP E

  LEFTJOIN DEPT D

    ON E.DEPTNO = D.DEPTNO

这条语句非常简单,就是获取EMP表与DEPT表内关联后的数据量。在看具体的执行计划之前,我们解读下在常规情况下,DB是如何处理这样的数据的

1、分别读取emp表和DEPT表的数据;

2、对EMP中的DEPTNO与DEPT表中的DEPTNO进行内关联;

3、对内关联后的数据进行汇总计算;

4、返回汇总计算结果。

也就是说会存在EMP与DEPT表的内关联,因为SQL就是这样写的。那我们看下该语句的执行计划,如下:

ORACLE优化器果真是按照我们的预想制定了执行计划。

1唯一性字段对执行计划的影响

由于在模型分析时,我们发现DEPT表的DEPTNO字段是唯一的。于是我们需要通过如下语句为该字段创建主键:

ALTERTABLE DEPT ADDCONSTRAINT PK_DEPT PRIMARYKEY(DEPTNO);

我们再回过头来看执行计划,会发生变化吗?

如果此时的你还不能看出问题,那么我们就对比下DEPT表的主键创建先后执行计划的变化:

俗话说:不比不知道,一比吓一跳。DEPT莫名其妙的被ORACLE优化器弄“丢”了。这不禁让人怀疑:这样的裁剪是否是不负责任的?也就是说,裁剪后的结果是否会因为裁剪而发生变化?在深入了解到LEFT JOIN的原理及模型结构后,你就会明白为何ORACLE优化器在DEPT表创建了基于DEPTNO字段的主键后,会做这样的裁剪。

支持ORACLE做如此大胆裁剪的理由是:

1、 LEFTJOIN在没有where条件过滤的时候,是不会减少结果数据量的;

2、 如果被关联的字段是被关联表的主键(或者唯一性字段),那么是不会使结果数据量增多的。

既然结果集的数据量不增加也不减少,那为何还要多访问一个表,多做一次关联呢?这就是ORACLE的精明之处:简单的就是高效的。

接下来,我们继续上面的实验(当然是基于上面的模型基础,即在DEPT表上创建了基于DEPTNO字段的主键)。这次,我们将LEFT JOIN改成INNER JOIN,看看执行计划是怎么样的:

表结构和约束关系没有发生变化,消失的DEPT又回来了。

神马原因呢?LEFT JOIN是不会有数据过滤的作用的,但是INNER JOIN则有过滤的功用。

为了验证,我们准备如下数据:

INSERTINTO dept(deptno,dname)VALUES(14,'财务');

INSERTINTO dept(deptno,dname)VALUES(31,'行政');

INSERTINTO EMP(EMPNO, ENAME, DEPTNO)VALUES('001','张三',14);

INSERTINTO EMP(EMPNO, ENAME, DEPTNO)VALUES('002','李四',31);

INSERTINTO EMP(EMPNO, ENAME, DEPTNO)VALUES('003','王五',21);

INSERTINTO EMP(EMPNO, ENAME, DEPTNO)VALUES('004','麻六',14);

现在来看看LEFT JOIN和INNER JOIN的不同结果:

也就是说,LEFT JOIN和INNER JOIN还是有差异的,那么在什么情况下才能在执行计划中将DEPT“枪毙”掉呢?

2主外键约束对执行计划的影响

我们对EMP和DEPT创建一个主外键约束(在创建主外键约束前,我需要删除掉empno=’003’的记录):

ALTERTABLE EMP ADDCONSTRAINT FK_DEPTNO FOREIGNKEY(DEPTNO)REFERENCES DEPT(DEPTNO);

看看效果如何:

这样是不是已经非常明确了DEPT再度消失的原因了?因为创建了主外键,也就是等于说EMP所有的DEPTNO必须要存在DEPT表中,既然有这样的约束,那自然就不需要多此一举的关联DEPT表了。

3字段属性对执行计划的影响

现在我们往EMP表里面再添加一条数据:

INSERTINTO EMP (EMPNO, ENAME, DEPTNO)VALUES('005','赵七',NULL);

再看看INNER JOIN的结果:

结果只有3条数据,明显刚才新增的数据是被过滤掉了,因为他的DEPTNO为null,其null并没有存在于dept表中。

而在执行计划里面,是没有DEPT表的:

也就是说该SQL就应该等价于如下SQL:

SELECT E.EMPNO, E.ENAME

 FROM EMP E

我们再看结果:

不对呀,说好的等价呢?难道是执行计划出了问题?还是我们对执行计划的理解错了?也或许是执行计划对我们隐藏了什么?

以上,我们都是在ORACLE的第三方开发工具PL SQL DEVELOPER里面查看的执行计划。现在我们换种方式,在SQL PLUS里面通过explain plan这种最原始的方式来查看执行计划,如下:

原来,在这个执行计划的内容中,明显的多出了“Predicate Information”,而在这部分信息里面,filter是:E.DEPTNO IS NOT NULL。

好吧,我们先把这个谓词放进SQL中,看看效果:

果然,我们发现,增加了这个谓词后,两个SQL又等价了。此时,我们会想:天哪,如果再遇到其他场景,会不会又不等价了?


在关联条件存在主外键关系约束的前提下,如下两个SQL是等价的:

不管你信不信,反正我信了

而此时,我们来看看EMP.DEPTNO的字段属性:

我们发现其Nullable属性为true,即可为null值。而如果我们将该属性值修改为false呢?

DELETEFROM emp WHERE empno ='005';

COMMIT;

ALTERTABLE EMP MODIFY DEPTNO NOTNULL;

再看执行计划:

我们发现原来的“Predicate Information”不见了,也就没有了E.DEPTNO IS NOT NULL的谓词约束。

4程序员与ORACLE的较量

在上面,我们在极力“宣传”着oracle是多么多么的智能化,而事实上,她的智能程度也是存在局限性的,比如她对SQL语句的取舍绝对的依赖于物理模型结构及约束,而一旦这种物理模型结构及约束不存在,那么ORACLE这位“巧妇”显然也只能“难为无米之炊”了。即便我们在SQL中进行了(唯一性)约束,ORACLE也会选择视而不见,比如如下SQL:

SELECTCOUNT(1)

FROM EMP E

LEFT JOIN (SELECTDISTINCT DEPTNO FROM DEPT) D

ON E.DEPTNO =D.DEPTNO;

按照我们在上面的理解,由于在子查询D中,已经对DEPTNO进行了distinct处理,也就意味着在子查询D中,DEPTNO绝对是唯一性的,即子查询D对整个SQL返回的结果是没有任何影响的,该SQL完全等价于如下SQL:

SELECT COUNT(1) FROM EMP E

而事实上呢,我们看看ORACLE的执行计划:

这一次很让我们意外,ORACLE居然没有识别出子查询D的作用。由此看来,在某些时候,尤其是在错综复杂的业务逻辑面前,oracle往往束手无措,远没有程序员聪明,所以在ORACLE这位巨无霸面前,我们也大可不必妄自菲薄。

5总结

至此,我们可以为第一个主题做出如下总结:

1、ORACLE优化器为达性能之目的,会不择手段的简化Operation;

2、ORACLE优化器的手段之一就是充分利用数据库约束,包括但不局限于:唯一性约束、主外键参照性约束、Nullable约束;

3、在约束条件内,ORACLE会简化SQL,在Operation时不再重复约束;

4、因此,在日常模型设计时,应尽可能的建立约束,最大程度上减少重复约束带来的“非战斗性减员”,从而提升SQL性能

完整的执行计划

在上一节的最后示例中,为了更全面阐述问题,我们“抛弃”了在PL SQL DEVELOPER通过F5得到的执行计划,转而选择了最原始最古老的explain。因为我们发现:

这几列还不足够支撑我们了解ORACLE优化器的意图,或者说还不够让我们拼凑出ORACLE优化器对SQL改写后的全貌。至少我们还需要谓词(Predicate)。所以,一个完整的执行计划除了:访问方式(表访问、索引访问等)、连接方式(NESTED LOOP、HASH JOIN等)及访问顺序外,还应包括谓词(Predicate),通过结合谓词,我们更能还原ORACLE优化器对SQL做了哪些改动?


为了直观期间,我们还是继续在PL SQL DEVELOPER中演示,只是执行计划的正确打开方式是这样的:

那么我们能从谓词中发现什么呢?

我们都知道,在表的统计信息采集及时的场景下,如果某个索引字段存在条件过滤,而执行计划中没有通过索引访问,而是table access full。那么原因无非就是:该过滤条件值的数据量太大(比如超过全表数据量的20%),或者是SQL的写法不当(该字段上应用了函数、表达式等)。

其实,除了上述两种场景外,还有一种场景也会导致table access full。我们先来看一个非常简单的案例,我们在EMP.DEPTNO上创建一个索引,因为经常会遇到查询某个特定部门的员工信息。

CREATEINDEX EMP_I1 ON EMP(DEPTNO);

因为在DEPT表中,DEPTNO的数据类型为NUMBER(2),在查deptno为14的员工信息时,我们会习惯性的写成:

SELECT*FROM emp WHERE deptno =14;

我们的如意算盘是通过索引EMP_I1来访问EMP表。而事实上,从执行计划看,却是table access full的访问方式:


尽管deptno=14的数据量为0,并且也没有在deptno上有任何的函数或者表达式。那么问题出在哪里呢?

我再来看看谓词:

很明显,在实际的执行过程中,DEPTNO是被TO_NUMBER函数包了一层,自然就走不了索引。那么是什么让ORACLE如此“昏庸”,以致“无事生非”的添加一个函数呢?

我们再看看EMP.DEPTNO的数据类型:

原来,EMP.DEPTNO的数据类型并没有同DEPT.DEPTNO保持一致,被设计成了VARCHAR2。因此要想走索引,就有三种办法:将DEPTNO的数据类型修改为NUMBER(2)、创建TO_NUMBER(DEPTNO)的函数索引、将过滤条件有之前的DEPTNO=14修改成DEPTNO=’14’。


我们就看下第三种方案的执行计划:

这才是我们想要的执行计划,却不是我们想要的表模型。这三种方案孰优孰劣不在本次分享主题范围内,如有机会,再行讨论。

没错,这就是隐式强行转换的风险,而所有的字段隐式转换在执行计划中都会被“曝光”

隐式转换都是“无意为之”,有两种场景:其一是对过滤字段的数据类型“想当然”的认为;其二是对过滤值类型的错误判断。刚才的案例属于第一种,那么第二种又是怎么回事呢?

以下是一个真实的案例:

系统中存在一个日志表,数据量非常大,我们对日志表按照日志时间(log_date)做了分区。在页面,要求强制按照log_date过滤,以命中分区而提高效率。但是分区+强制过滤并没有收到预期的性能效果,但是将同样的查询条件直接在DB中执行却非常快。通过对比执行计划发现,通过页面调用执行时,并没有命中分区,而在访问谓词中,log_date字段过滤时,多出了函数INTERNAL_FUNCTION。也就是将log_date字段隐式强制转换成了timestamp。而导致这种问题的原因是JAVA数据类型与ORACLE数据类型之间的转换出现了问题。最后通过JAVA传STRING到ORACLE,然后在SQL中将变量值TO_DATE成DATE类型解决。

我们也可以简单模拟下。比如我们在EMP中创建基于HIREDATE的索引:

CREATEINDEX EMP_I2 ON EMP(HIREDATE);

我们现在要查找今年以来入职的员工信息,SQL如下:

SELECT*FROM EMP WHERE HIREDATE >SYSDATE

其执行计划如下:

而如果我们将date’2016-01-01’转换成timestamp,则执行计划如下:

在这个案例中,如果不查看谓词,就很难找到性能的根源。

朴实的执行计划

我们继续执行计划中的“谓词”,看看还能给我们哪些意外之喜?

在上个章节中,我们注意到,在查询今年入职的员工信息是,我们用了DATE’2016-01-01’。这种写法很少见诸于正式书籍中,因为这是非标准写法。那么将VARCHAR2转换成DATE的标准写法是什么呢?

执行计划会告诉你:

原来DATE’2016-01-01’被转换成了TO_DATE(‘2016-01-01’, ‘SYYYY-MM-DDHH24:MI:SS’),这样是为什么DATE只能支持YYYY-MM-DD格式的字符串的原因:

可见,ORACLE优化器会将SQL中一些非标准的写法转换成标准的朴实的写法。有的时候,最朴实的写法,最容易让人理解。

比如,当你拿到如下SQL时:

SELECT ENAME, SAL

  FROM EMP

 WHERE SAL >SOME(SELECT SAL FROM EMP WHERE DEPTNO =10);

你会不会很懵菜?会不会去查资料,研究SOME的作用和用法?或许大半天后,你仍然被SOME\ANY\ALL弄得云山雾罩的。

现在,我们试着从执行计划去探究>SOME的含义。

我们将子查询替换成具体的LIST(100,200,300),发现在执行计划中,谓词变成了SAL > 100,意思就是大于最小值。换言之,原来的SQL就是要查询大于DEPTNO=10部门最低工资的员工信息。

文章转自数据和云公众号,原文链接

时间: 2024-09-17 03:59:13

【云和恩墨大讲堂】从执行计划洞察ORACLE优化器的“小聪明”的相关文章

【云和恩墨大讲堂】复合索引与绑定变量

编辑手记:2016 Oracle 技术嘉年华活动已经结束,为了让更多的技术爱好者有机会获取大会干货资源,我们将不断邀请演讲嘉宾展开"2016 Oracle 线上嘉年华". 讲师简介 邓秋爽(小鱼) 云和恩墨专家,有超过5年超大型数据库专业服务经验,擅长oracle 数据库优化.SQL优化和troubleshooting 今晚的恩墨大讲堂将有我为大家分享SQL审核中的两个典型案例,如下: 1复合索引前导列选择 2绑定变量分享--某业务SQL性能问题分析 在介绍这两个case之前先对目前我

【云和恩墨大讲堂】Oracle线上嘉年华第二讲

编辑手记:Oracle线上嘉年华,正在持续分享中.本次的主题是系统割接中的SQL解析问题和结合业务的SQL优化改写技巧. 1嘉宾介绍 小鱼(邓秋爽) 云和恩墨专家,有超过5年超大型数据库专业服务经验,擅长oracle 数据库优化.SQL优化和troubleshooting 新系统割接的library cache问题 这是我们在做系统割接的时候的一个案例,可能并不是很常见,这个案例是将Oracle 11g升级到12c的时候遇到的问题,出现了大量的library cache的问题.具体情况是: 新系

【云和恩墨大讲堂电子期刊】挑战者:Google成功的背后

致亲爱的读者: 在商业竞争中,进攻常常也是最好的防守.Google从小到大一直贯彻这个原则.Google在规模很小的时候,受到很多比自己大的公司的围攻,死守是没有出路的.因此它把自己放在一个挑战者的位置,不断挑战对手,也挑战自己.在商业竞争中,Google常常是两眼盯着前方向前冲,而不左顾右盼它的对手. 这段话摘自<浪潮之巅>,也是我认为对于Google成功的背后最好的阐释.Google公司从一开始就以挑战者的身份出现在人们的视野中,它不仅在技术和商业上挑战比自己更大更强的公司,而且在理念上挑

【云和恩墨大讲堂】SQL玩转AWR裸数据

编辑手记:Oracle线上嘉年华第四讲,SQL玩转AWR裸数据,教你真正利用AWR中的裸数据对系统性能进行分析并调优. 作者简介: 罗海雄 云和恩墨优化专家 ITPUB论坛数据库管理版版主,2012 ITPUB全国SQL大赛冠军得主,他还是资深的架构师和性能优化专家,对 SQL 优化和理解尤其深入:从开发到性能管理,他有着超过10年的企业级系统设计和优化经验.曾经服务于甲骨文公司,组织和主讲过多次<甲骨文技术开发人员日>和<Oracle圆桌会议>,并具备丰富的制造行业系统架构经验.

【云和恩墨】内外兼修:Oracle ACED熊军谈Oracle学习

原创 2016-07-07 熊军  编辑手记:熊军是中国西部第一位,也是到目前为止唯一的Oracle ACE总监,在这篇文章中熊军描述了他的学习过程和理念供大家参考. ORACLE 的学习,就好比武侠小说中学武功.要从三方面入手: 1.内功:针以学习 ORACLE 来说,内功就是对基本概念的掌握,ORACLE 架构的深入理解,原理的掌握.如果有兴趣和时间,研究一下 Internal 的东西,这好比修习易筋经,需要极大的毅力和长期的坚持. 2.招式:如果光有内功,没有招式,则会陷入空有高深内力,却

云和恩墨助力东风日产Exadata一体机核心系统

东风日产乘用车公司成立于2003年6月16日,是东风汽车有限公司旗下重要的乘用车板块,从事乘用车研发.采购.制造.销售.服务业务,是国内为数不多的具备全价值链的汽车生产企业. 自成立开始到 2014 年 8 月,东风日产累计整车产销量已突破 550 万辆,刷新行业最快纪录.在 2015 年,东风日产四大基地整车总产能有望从现有的 100 万辆提升至 150 万辆. 东风日产技术中心,是日产汽车公司在全球第四个具有同等研发平台的技术中心,是中国华南地区最大的乘用车技术中心,通过与世界同步的研发,不

云和恩墨助东风日产Exadata一体机营销核心系统

东风日产乘用车公司成立于2003年6月16日,是东风汽车有限公司旗下重要的乘用车板块,从事乘用车研发.采购.制造.销售.服务业务,是国内为数不多的具备全价值链的汽车生产企业. 自成立开始到2014年8月,东风日产累计整车产销量已突破550万辆,刷新行业最快纪录.在2015年,东风日产四大基地整车总产能有望从现有的100万辆提升至150万辆. 服务简介 2015年度,东风日产发布多款新车型,业务部门提出更高的追求.为了支持业务的快速发展,信息部门采购OracleExadata一体机作为核心交易系统

【云和恩墨】性能优化:Linux环境下合理配置大内存页(HugePage)

原创 2016-09-12 熊军  [云和恩墨]性能优化:Linux环境下合理配置大内存页(HugePage) 熊军(老熊) 云和恩墨西区总经理 Oracle ACED,ACOUG核心会员 PC Server发展到今天,在性能方面有着长足的进步.64位的CPU在数年前都已经进入到寻常的家用PC之中,更别说是更高端的PC Server:在Intel和AMD两大处理器巨头的努力下,x86 CPU在处理能力上不断提升:同时随着制造工艺的发展,在PC Server上能够安装的内存容量也越来越大,现在随处

《Oracle性能优化与诊断案例精选》——1.5 云和恩墨,数据服务起征途

1.5 云和恩墨,数据服务起征途 时至今日,IT这个行业仍然是最为吸引毕业生的一个重要行业.记得多年前榕树下的一位朋友"落花如雨"说过一句话:喜欢这个行业,因为这个行业里汇聚了这个时代最聪明的人才与最快速增长的财富. 就因为这两点,众多的年轻人前仆后继的开始涌入这个圈子.那么然后,出路又在何方呢?一直以来大家都认为,程序员或者IT领域是年轻人的天下,因为这里有变换迅速的技术和产品,而机遇和压力一直是呈正比增加的. 我也开始探索作为技术人的出路,云和恩墨就是这样一个开始,如图1-8所示.