作为一个开发人员,开始认真系统学习Oracle SQL也只有月余,之前的SQL知识仅仅是项目里面用到什么就去看什么,简单说就是Read The Fxxking Guide。在系统学习Oracle SQL之前,俺的想法就是“按照需求完成功能,剩下的交给DBA来处理”。这个想法从整体来看显而易见是错误的,但若分割成两个独立的分句“按照需求完成功能”与“剩下的交给DBA来处理”却又是正确的。接下来详细阐述我的观点。
你的问题是什么?
这个问题应该无时无刻地回响在我们的脑海中。我们必须清晰地了解我们面对的问题是什么?否则就会演变成一句老话“进去的是垃圾,出来的还是垃圾!”。而SQL调优的最牛的境界就是不需要继续运行那个SQL(因为无论如何调优,那条SQL语句仍然会占用资源,而不运行该SQL,则它的cost就是零,没有比这个更牛X的了),要做到这一步的前提,显然是充分理解所需处理的问题。不知道问题的来龙去脉,不知道写该SQL的目的,或者是不清晰明白“我的问题是什么”,写出来的SQL的也是蹩脚SQL,也是被无数DBA深恶的SQL,也很可能成为应用的性能murder。也是基于此原因,开发人员也应该熟练掌握基本SQL语法,基本的调优原则,不要写出有明显性能问题的SQL语句。并且这是开发人员应该做的,且必须做到的,因为我们才是切实明白或者说更应该理解我们所需面对的问题的人,我们才是对后续SQL调优的最佳人选,而且DBA很难通过SQL语句去了解开发人员写该SQL的目的,这一点就剥夺了DBA从本质上提升性能的机会,留给他们的只能是无尽的猜测,hint,运气好的话,还能找到那个被诅咒的开发人员问问写那条SQL的原因何在。当然,我由衷的希望这个DBA没有带刀 :)
在明确了“问题是什么?”后,进一步就是按照正确的理解去实现SQL。开发人员应该“按照需求完成功能”,不能完成功能的SQL都是垃圾,不论其性能多么优异,写得多么漂亮。就如,一条SQL本该返回100条数据,而实际却只返回了99条,那么哪怕是换一种写法能返回100条数据,但是性能比目前的返回99条数据的SQL要差很多,那么我们也应该选择能完成功能的SQL,99不是100,用户要的是100,而不是99。
别总拿性能来说事,它不一定就是问题。
我们的周边,总会有人以性能为借口,说你的这种写法会有性能问题,应改成XXXX写法,或者说我们因该对每一条SQL都使其性能最优。依我来看,这是一种危险的想法,首先犹如宇宙中某一个反夸克一样难以琢磨的CBO生成的执行计划在不同机器上的差异来说,在某台机器上执行得不错的执行计划,在另一台机器也许就是灾难,开发人员在开发环境中做的“提前优化”,也许在生产环境中就是不管用,乃至产生负作用。更有甚者忽视表的数据量规模,一味追求“性能”,他们的目的是显示自己对Oracle多“熟悉”,自己多“牛逼”,殊不知面对的数据量是千和万级别时,乃至十万级别,在Oracle面前这都是小菜一碟。一条SQL执行一次需0.1 ms,而另一种写法需0.2 ms一次,前者的性能是后者的1倍,但是在面对至少100ms的网络传输延迟面前,两种SQL的写法都无关紧要了,1/1000级别的性能提升毫无意义,倘若当初为了将0.2ms提升至0.1ms花费了大量精力,这也许对开发人员个人而言是有益的,但对整个项目来说这就是浪费。
总拿性能说事多多少少和提前优化相关,这也是一个老生常谈的问题了。
话说当年在贝尔实验室. 一群工程师围着一个巨慢无比的小型机发呆. 为啥呢, 因为他们觉得这个机器太慢了. 什么超频, 液氮等技术都用了, 这个小型机还是比不上实验室新买的一台桌上计算机. 这些家伙很不爽, 于是准备去优化这个机器上的操作系统. 他们也不管三七二十一, 就去看究竟那个进程占用CPU时间最长, 然后就集中优化这个进程. 他们希望这样把每个程序都优化到特别高效, 机器就相对快了. 于是, 他们终于捕捉到一个平时居然占50% CPU 的进程, 而且这个进程只有大约20K的代码. 他们高兴死了, 立即挽起袖子敲键盘, 愣是把一个20K的C语言变成了快5倍的汇编. 这时候他们把此进程放到机器上这么一实验, 发现居然整体效率没变化. 百思不得其解的情况下他们去请教其他牛人. 那个牛人就说了一句话: 你们优化的进程, 叫做 System Idle.
在此引用科学怪人高爷爷的话说, 提前优化是万恶之源 (Premature optimization is the root of all evil)
别曲解高老爷子的话,如果你真的十分明确某一部分倘若处理不好极有可能成为一个性能瓶颈,那么还是可以拿出你所有的本事去搞定它。在SQL这个话题里,除非你处理的是大量数据,否则还是不要轻易提前优化。大数据量时,需谨慎对待,你需要了解各种访问路径以及连接方式在大数据量时能产生的大致影响,如果能明确此时使用hash join肯定会比nested loop好,那么可以加上USE_HASH hint。然后再谨慎地验证自己的想法。在其他情况下,应尽量使SQL简单明了。
在该死的CBO面前一切都是神秘莫测的,但是我们因该相信它。
在Oracle引入CBO后,我们总是能听到某天某个SQL,CBO产生了逆天的执行计划,性能差到了没有终点。此处省去一万字对CBO的血泪批判。但是我要说的是,从概率来说,CBO产生的执行计划,大部分情况下是正确的。
给大家举个例子:
表tab1,有100万条数据, indexed_column是一个varhcar2(255)的not null列,而有一个索引idx_some_column建立在该列上,该索引既可以是B树索引,也可以是位图索引。Tab1表已经被完全统计过了。
Select * from tab1 where indexed_column = ‘a value’;
对上述sql,我们预期会返回14万条记录。我的问题是使用索引来读取这14万条记录快呢?还是走全表扫描快呢?
我无法给你一个100%的正确的答案,但是在大多数情况下,走索引会比走全表扫描慢。在获取14万条记录之前,大概会产生3×14万的single block I/O,再加上通过rowid获取14万条记录的single block I/O,总共接近56万个single block I/O。而在全表扫描时,只需要用multiblock I/O读取100万条记录,multiblock I/O的次数主要取决于当前表空间的block size大小和每条记录的大小。而我们知道对I/O而言,我们更喜欢大量少次的multiblock I/O,尽量避免少量多次的single block I/O。整体而言,全表扫描会比使用索引快,而CBO的选择也是如此。答案肯定会是这样吗?不!走索引也有比走全表扫描更快的时候,当该索引大部分都缓存在内存中时,访问索引的single block I/O成本就会很低,这样走索引也许就会比全表扫描快。但是,我们能确定缓存里面一定有该索引的缓存数据吗?这次访问时还存在缓存数据,下次访问时就一定存在吗?那么谁能比你更清楚当前缓存中是否包含该索引呢?CBO!所以请不要贸然地加上index hint。或者理所当然地认为使用索引会更快。
我们都知道CBO会综合考虑多方面的因素,来选择最后的最优执行计划。而随着环境的不同,CBO也会产生不同的执行计划。这也就是在一台机器跑得很好的sql,在另一台机器上就问题多多的原因。盲目地强制CBO生产某个特定的执行计划,就是灾难的开始。所以,我再次抛出我的观点,仅对需处理大数据量的SQL语句谨慎地调优,而其他仅仅会处理少量数据的SQL就由CBO全权负责。
Hint hint hint
谈到SQL调优,就不能不提及hint。Hint是我们改变CBO抉择的有效途径。而最常用的就是改变访问路径的hint和改变连接方式的hint。这两点对查询而言是至关重要地。在《Oracle Database 10g Performance Tuning Tips & Techniques》中列举了Top 9的最常用的hint。
INDEX
虽然索引并不总会快于全表扫描,但是很多时候我们希望Oracle使用索引来执行某些SQL,这时候我们可以通过index hints来强制SQL使用index.
Index Hints的格式如下:
/*+ INDEX ( table [index [index]...] ) */
ORDERED
用ORDERED来提示CBO按表的出现顺序来连接表。
SELECT /*+ORDERED*/ A.COL1,B.COL2,C.COL3 FROM TABLE1 A,TABLE2 B,TABLE3 C WHERE A.COL1=B.COL1 AND B.COL1=C.COL1;
PARALLEL
FIRST_ROWS
提示CBO生成的执行计划是以最小响应时间来返回记录。在需要快速获取前N行时有用。
SELECT /*+FIRST_ROWS*/ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO='SCOTT';
FULL
表明对表选择全局扫描的方法.
SELECT /*+FULL(A)*/ EMP_NO,EMP_NAM FROM BSEMPMS A WHERE EMP_NO='SCOTT';
LEADING
将指定的表作为连接次序中的首表.
USE_NL
将指定表与嵌套的连接的行源进行连接,并把指定表作为内部表.
SELECT /*+ORDERED USE_NL(BSEMPMS)*/ BSDPTMS.DPT_NO,BSEMPMS.EMP_NO,BSEMPMS.EMP_NAM FROM BSEMPMS,BSDPTMS WHERE BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;
APPEND
USE_HASH
将指定的表与其他行源通过哈希连接方式连接起来.
SELECT /*+USE_HASH(BSEMPMS,BSDPTMS)*/ * FROM BSEMPMS,BSDPTMS WHERE BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;
除去PARALLEL和APPEND 以外,剩下的就是开发人员用得最多的hint。
访问路径与连接方式,使用hint前的必修内容
要想从本质理解何时使用hint,使用何种hint就需要了解oracle的访问路径。你至少需要知道全表扫描,几种索引扫描和nested loop join,hash join以及merge sort join.。更多更详细的说明,请参考Oracle的官方资料。
为什么没有使用索引呢?
关于索引有无数的话题可以谈,哪怕是关于索引本身就有很多内容。最常用的索引为B 树索引,位图索引以及函数索引。想了解索引的细节和种类可以参阅Oracle的文档。在这里只简单的描述一下几个常见的未能使用索引的原因,更多信息请查找TOM的著作,里面会有较为详细的讲解。在说明之前,我们一定要明白使用索引并不一定能加快SQL的执行速度。
第一个原因,使用索引会使查询更慢。
关于这个问题,先前已有例子说明,再次只需强调一下“使用索引就一定快”不是神话,而是彻头彻尾的流言。
第二个原因,显式或隐式对有索引的列使用了某个函数。
Select * from table_a where f(indexed_column) = some_value;
此时由于对indexed_column使用了某个函数f(),因此,CBO将不会使用建立在该列的索引。同样,若indexed_column为Number,下面的SQL也不会使用在indexed_column上的索引
Select * from table_a where indexed_column = ‘5’;
该SQL实际会被改写成
Select * from table_a where to_char(indexed_column) = ‘5’;
这就是由于类型不匹配导致隐式使用函数的例子。
其他一些需要注意的地方
不要轻信流言。
我曾经听说过distinct比group by的性能更优异。这完全是错误的,我在多台机器上测试过,distinct ‘all columns’ 100W条数据与group by ‘all columns’100W条数据,两者所用的时间基本相等,逻辑读数完全一致。而在小数据量下,还需要比较吗?因此,那种方式更贴合“我们的问题”就用那种,请无视两者的性能差异—它们没有“差异”。
绑定变量的窥视对SQL执行计划的影响。
毫无疑问,减少硬解析,跟多地使用软解析会对oracle性能带来提升。
Select * from a_table where a_col = :value
如果有这么一句sql,oracle在执行时,会尝试获取绑定变量value的数据类型。假如a_col是一个varchar类型的列,并且有一个索引建立在该列上,那么在第一次运行时该sql时,value的类型若为varchar,那么CBO将有可能会选择索引访问路径,若value的类型为其他类型,由于会发生隐式类型转换,将使CBO不在选择使用索引的执行计划。而窥视的影响则在此之后,当以后该SQL再次执行的时候,CBO仍会选择之前的执行计划,而不论此次执行时value的数据类型是什么。最终,即便如此,我们仍然应该尽量多地使用绑定变量。
EXISTS or IN
呵呵,这也是一个老话题了,别迷信坊间传言,没有那个一定比另一个好。我就跑出过使用exist或in产生出完全一样的执行计划,并且拥有一样的逻辑读的情况。不过按照《Oracle 10gR2 Performance Tuning Guide》的说法,在subquery中具有高度可选择性的条件时,应该使用in,相反,在parentquery里具有高度可选择性的条件时,应该用exists。
为什么说要把剩下的留给DBA?
很简单,开发人员可以做很多事情,他们最了解功能需求,因此他们具有最多的调优空间,但是我们不能强求每一个开发人员都很了解数据库。对,他们的确应该具备基本的调优能力,但是这不代表他们需要去了解数据库的方方面面。例如,改变表数据对应的buffer cache的类型,在一些情况下的确会实质性地提升性能。开发人员必需了解这个吗?指定Buffer cache的类型需要在crate table或alter table时操作,需要把这个权限开放给开发人员吗?OPTIMIZER_INDEX_CACHING与 OPTIMIZER_INDEX_COST_ADJ这两个oracle参数对CBO影响巨大,这个一般是完全靠oracle自己收集信息以调整,或者是由有经验的DBA预置,再让oracle接手。这个开发人员必需知道吗?share pool的大小,log buffer的大小,乃至SGA,PGA这些都影响着oracle的运行,但这些开发人员必需知道吗?或者说需要他们去动手调整吗?这就更不用提 RMAN,ASSM,ASM,AMMS这些东东。不!开发人员必需知道的是SQL语法,单行函数,聚合函数以及基本的SQL调优手段,这里包括各种访问路径,连接方式以及改变这些访问路径和连接方式的hint,除此之外让DBA来做,因为那些领域是DBA专长,开发人员无法替代他们,或者说无法比他们做得更好。当然,某个/些开发人员对数据库有爱就另当别论了。
在最后,总结一下的我想法。谨慎地对需处理大量数据的SQL进行调优,而在仅需处理小数据量的SQL时,尽量保持简单明了。在进行任何调优动作之前,请先重新收集最新的统计信息。
分享到:
相关推荐
- **SQL*Plus**:一个命令行工具,用于执行SQL语句和PL/SQL块,也是预编译器的重要组成部分之一。 - **SQLJ**:允许Java应用程序直接嵌入SQL语句,通过编译器处理这些语句,使其能够在Java环境中高效运行。 - **...
Oracle Call Interface (OCI) 是一个强大的应用程序编程接口(API),允许开发人员使用 C 语言编写高性能的应用程序。它提供了与 Oracle 数据库进行交互的功能,并支持所有 Oracle 功能。Oracle 11g Release 2 (11.2...
总结,"Oracle Call Interface Programmer's Guide(19c)"涵盖了使用Oracle C接口进行数据库开发的所有关键方面,从基础的连接和SQL执行到高级的特性如集合、PL/SQL调用和事务管理。对于任何希望在C环境中与Oracle...
OCCI 是 Oracle 数据库的一个强大工具,为 C++ 开发者提供了与数据库交互的能力。通过掌握 OCCI 的基本概念和使用方法,开发者可以构建出高性能、可靠的应用程序。无论是初学者还是经验丰富的开发者,都能从 OCCI 中...
### Oracle 11g Pro*C/C++ Programmer's Guide 关键知识点概述 #### 一、文档基本信息 - **标题**: Oracle11g_Help - **描述**: Pro*C/C++ Programmer's Guide 11g Release 1 (11.1) B28427-03 - **版本**: 11g ...
总结来说,这份培训资料将覆盖Oracle的基本操作、数据安全、备份策略、SQL语言的使用以及数据库的日常管理,对于初学者和进阶者都是宝贵的学习资源。学习和掌握这些知识,将有助于在实际工作中有效地管理和维护...
标题与描述中的关键词“Rational Application Developer programmer”指向了IBM Rational Application Developer(RAD)这款集成开发环境(IDE),主要用于构建企业级应用,如Java、Web、XML、数据库、EJB、Struts、...
Oracle Certified Professional(OCP)是Oracle公司为专业人士提供的数据库管理员和开发人员的认证,它证明了个人在Oracle技术领域的专业知识。OCP考试模拟器是一种工具,帮助考生在实际考试前熟悉考试格式、试题...
根据给定的文件信息,以下是详细的知识...这些知识点是根据提供的文件信息提炼出来的,如果需要更深入的学习,可以参考Oracle官方文档,如Oracle® Call Interface Programmer's Guide,或者Linux内核的相关技术资料。
这个文件名暗示了这是一个关于PowerBuilder的具体应用实例,可能包含了数据窗口的使用、业务逻辑处理、用户界面设计等内容,对于初学者来说是一个很好的参考案例。 在PowerBuilder中,数据窗口是核心组件之一,它...
继承允许一个类(子类)继承另一个类(父类)的属性和方法,增强了代码的复用性。 3. **包与导入**:包是组织Java类的一种方式,可以避免命名冲突,如`java.util`、`java.awt`等。`import`语句用于引入所需包中的类...
6. **证书与奖励**:如果你有获得的相关认证(如Oracle Certified Professional, Java SE 8 Programmer)或者在学校或比赛中获得的荣誉,也可以一并列出,以增强简历的吸引力。 7. **自我评价**:简短地总结你的...
【Java简历模板】是程序员在求职过程中展示自身技能和经验的一种文档,对于Java开发者来说,一个详实且专业的简历能够有效地吸引潜在雇主的注意。以下是一份基于Java技术的简历模版及其相关知识点: **基本资料** ...
7. **证书与奖励**:如有Java相关的认证(如Oracle Certified Professional, Java SE 8 Programmer)或者获得的荣誉,不妨一并列出。 8. **自我评价**:展示你的职业素养和个人特质,强调团队协作能力和问题解决能力...
9. **证书与奖项**:如有获得Oracle Certified Professional, Java SE 8 Programmer等认证,或者在学校或比赛中获得的荣誉,都可以提升简历的吸引力。 10. **格式与排版**:简历应该清晰、整洁,使用标准字体,避免...
以下是一个适用于IT行业的英文简历模板,它包括了必要的个人信息、职业目标、教育背景、技能与经验、项目经历以及参考信息。 首先,简历的顶部应包含求职者的姓名、联系方式(包括地址、电话和电子邮件),如: ```...
12. **证书与荣誉**:如果有Oracle Certified Professional, Java SE 8 Programmer等证书,或者在编程竞赛中获奖,记得一并列出。 13. **格式与排版**:简历的外观要整洁,字体和字号要统一,避免错别字和语法错误...
2. **数据库操作**:了解并能操作数据库是必要的,如Oracle,需要熟悉SQL语句,能够进行数据查询、增删改查操作,并懂得JDBC连接数据库的方式。 3. **Web框架**:求职者应熟悉SSH(Spring、Struts、Hibernate)框架...
- **与数据库对比**:与Access、Oracle、SQL Server等数据库相比,XML更侧重于数据展示而非提供复杂的数据管理功能如数据索引、排序等。 #### 二、XML的应用场景 - **跨平台数据交换**:XML因其简单性成为了不同...