常用的PL/SQL开发原则 by dbsanke

 

       在微博上看到dbsnake 有一个培训,因为是在北京举办的,过去不。所以问dbsnake 能否把相关的文档传我一份。 dbsnake 邮件给我时说有些东西没有写到文档里。 这个文档也是dbsnake 对Oracle 的一个经验的积累的过程,全部写出来也不实际,所以有机会还是多参加这类培训,这对技术提高还是很有帮助的。在这个文档里dbsnake 整理了一个PL/SQL 的开发文档。 贴过来,共享之。

 

常用的PL/SQL开发原则 bysnake:

       (1)广泛使用绑定变量,特别是批量绑定,因为这可以有效的避免sql的硬解析和PL/SQL引擎和SQL引擎的上下文切换!

       (2)广泛使用UROWID来处理DML语句

       (3)在你的存储过程中谨慎使用DDL语句,因为这可能会破坏你的transaction的连续性,更为严重的是可能会阻塞DML操作并可能会导致大量library cache latch争用并且有可能会导致某些sql执行计划的改变。

       (4)不要在存储过程里不应该commit的地方commit,特别是当你的存储过程会被另外一个存储过程调用的时候,你考虑到了你这么做会破坏调用你的父存储过程的transaction的连续性了吗?

       (5)注意你面对的数据量,小数据量的处理方法和海量数据的处理方法是不一样的!

       (6)循环的时候要注意清空临时变量的值

       (7)注意“select into 变量”的问题,使用子begin语句封装“select into 变量”以避免可能会出现的错误,这样就可以避免要在“select into 变量”之前先执行一下select count(*)

       (8)不要让oracle执行你的PL/SQL代码时产生隐式转换

       (9)在PL/SQL中定义varchar2变量的时候当你在不知道你所定义的变量的长度的时候可以将其定义为varchar2(4000),这一点都不浪费!

       (10)如果你写的一组存储过程有逻辑上的关联,那我建议你要把这些存储过程封装到一个package里面

       (11)改正你的PL/SQL代码里的所有编译时编译器提示出的warning

       (12)循环的时候一定要注意exit,否则就太可怕了!

       (13)处理显式cursor的时候一定要注意fetch和exit,否则就太可怕了!如下是我处理的一个真实的案例,这个案例中暴露出的问题就很好的说明了处理cursor的时候一定要注意fetch和exit:

       今天下午1点多的时候接到同事的一封信,信里提到我们的一个10gR2的开发环境连不进去了,报这样的错“ORA-00257:archiver error.Connect internalonly,until freed.”。很明显是因为归档进行不下去而导致整个系统被hang住了。

       我上去一看,果然是存放归档日志的目录满了。上述开发环境的存放归档日志的目录大小为10个G,alert log显示从今天11点半开始到下午1点左右的这段时间系统整整产生了10个G的归档日志,而且还在源源不断的继续产生。在继续产生归档日志的时候因为空间满了,所以导致整个系统被hang住。原因知道了,处理过程就很清晰了。

       只保留一个归档日志,然后手工把所有其他的归档日志rm掉,注意这里不要一下子就把所有的归档日志给rm掉,否则可能导致文件系统的句柄无法释放,进而这些归档日志占用的空间也无法释放。然后run一个ADDM报告,看一下是什么导致了在这么短的时间内产生了这么多的归档日志。从ADDM报告中可以很清晰的看到产生这么多归档日志的根本原因是因为一个存储过程里的几条sql在短时间内执行了1400多万次的缘故。

       用alter system kill session配合kill -9杀掉这个存储过程所在的session,以阻止它源源不断的产生归档日志。

       接着我分析了一下上述存储过程,发现根本原因在于在这个存储过程里打开了一个cursor,fetch这个cursor中的一条记录到一个record里,然后开启一个循环,在循环里run上述的那几条sql语句。但致命的是在循环的末尾并没有再fetch这个cursor中的另外一条记录,这就导致了上述循环的条件永远为真,所以上述sql如果不被我中断的话就会永远执行下去。这也就解释了为什么会在短时间里产生了大量的归档日志,并且undo tablespace的空间也被消耗殆尽。

后续处理过程这里省略……

       (14)bulk collect into的时候不要一次collect太多的数据,建议一次collect的数据量在10000条以内,你可以用批量绑定自带的limit子句来限制或者使用rownum来限制

       (15)如果你使用了批量绑定,那为什么要把时间浪费在写诸如insertinto tablename(column1,column2,……,column100) values(value1(i),value2(i),……,value100(i))这样的sql语句上面?如果有可能,就写一个你自己的存储过程代码生成器吧,让它来帮你生成这样的语句。你应该把精力集中在更有用的方面!

       (16)你希望你的代码被并发执行吗?如果你不希望或者你的代码根本就不能够被并发执行,那就想一个办法控制并发吧!在应用层面控制就好,比如在update之前先尝试对该记录加for update nowait的锁,或者利用DML语句当前读的特性来避免并发都是不错的主意

       (17)不要写诸如insert into tablename1 select* from tablename2这样的语句,你考虑到扩展性了吗?假如以后tablename1或者tablename2增加或减少字段了呢?

       (18)谨慎使用hint,除非你很清楚你在做什么。比如说这里你强制oracle使用了某个索引,假如以后这个索引的名字被改了,由此带来的执行计划的变更你怎么办?你考虑到这种情况了吗?

       (19)注意关联更新丢失数据的问题,update语句如果没有指定where条件那就是对所有的数据做update操作,这个就太恐怖了!

       (20)用好临时表,有时候临时表很有用!特别是在根据一堆复杂条件去更新海量数据的时候

       (21)尽量避免在存储过程里使用递规!不是说不能用递规(递规在某些特定的情况下很有用),而是说在用递规的时候一定要避免无限递规的情况!

       (22)写好你的PL/SQL代码里的注释,这个很重要!不写注释并不代表你很厉害!

 

 

 

 

 

 

-------------------------------------------------------------------------------------------------------

Blog: http://blog.csdn.net/tianlesoftware

Email: dvd.dba@gmail.com

DBA1 群:62697716(满);   DBA2 群:62697977(满)  DBA3 群:62697850(满)  

DBA 超级群:63306533(满);  DBA4 群: 83829929  DBA5群: 142216823   

DBA6 群:158654907  聊天 群:40132017   聊天2群:69087192

--加群需要在备注说明Oracle表空间和数据文件的关系,否则拒绝申请

posted @ 2011-07-19 20:13  生活不是用来挥霍的  阅读(135)  评论(0编辑  收藏  举报