使用Oracle数据库的应用系统,有时出现SQL性能忽然变差,特别是对于OLTP类型系统执行频繁的核心SQL,若是出现性能问题,一般会影响整个数据库的性能,进而影响整个系统的正常运行。这是经常遇到的问题,也是一些DBA的挑战。
sql
SQL的性能变差,一般是在SQL语句从新进行了解析,解析时使用了错误的执行计划出现的。数据库
下列状况是SQL会从新解析的缘由:ide
SQL语句没有使用绑定变量,这样SQL每次执行都要解析。性能
SQL长时间没有执行,被刷出SHARED POOL,再次执行时须要从新解析。测试
在SQL引用的对象(表、视图等)上执行了DDL操做,甚至是结构发生了变化,好比建了一个索引。优化
对SQL引用的对象进行了权限更改。对象
从新分析(收集统计信息)了SQL引用的表和索引,或者表和索引统计信息被删除。索引
修改了与性能相关的部分参数。文档
刷新了共享池。it
固然重启数据库也会使全部SQL所有从新解析。
SQL从新解析后,跟之前相比,性能忽然变差,一般是下列缘由:
1. 表和索引的优化统计信息被删除,或者从新收集后统计信息不许确。从新收集统计信息一般是因为收集策略(方法)不正确引发。好比对分区表使用analyze命令而不是用dbms_stats包、收集统计信息时采样比例太小等等。Oracle优化器严重依赖于统计信息,若是统计信息有问题,则很容易致使SQL不能使用正确的执行计划。
2. SQL绑定变量窥探(bind peeking),同时绑定变量对应的列上有直方图;或者绑定变量的值变化范围过大、分区数据分布极不均匀:
1) 绑定变量的列上有直方图:
假如表orders存储全部的订单,state列有3种不一样的值:0表示未处理,1表示处理成功完成,2表示处理失败。State列上有一个索引,表中绝大部分数据的state列为1,0和2占少数。有下面的SQL:
select * from orders where state=:b1
这里:b1是变量,在大多数状况下这个值为0,则应该使用索引,可是若是SQL被从新解析,而第一次执行时应用传给变量b1值为1,则不会使用索引,采用全表扫描的方式来访问表。对于绑定变量的SQL,只在第一次执行时才会进行绑定变量窥探,并以此肯定执行计划,该SQL后续执行时所有按这个执行计划。这样在后续执行时,b1变量传入的值为0的时候,仍然是第一次执行时产生的执行计划,即便用的是全表扫描,这样会致使性能不好。
2) 绑定变量的值变化范围过大:
一样假如orders表有一列created_date表示一笔订单的下单时间,orders表里面存储了最近1年的数据,有以下的SQL:
select * from orders where created_date >=:b1;
假如大多数状况下,应用传入的b1变量值为最近几天内的日期值,那么SQL使用的是created_date列上的索引,而若是b1变量值为5个月以前的一个值,那么就会使用全表扫描。与上面描述的直方图引发的问题同样,若是SQL第1次执行时传入的变量值引发的是全表扫描,那么将该SQL后续执行时都使用了全表扫描,从而影响了性能。
3) 分区数据量不均匀:
对于范围和列表分区,可能存在各个分区之间数据量极不均匀的状况下。好比分区表orders按地区area进行了分区,P1分区只有几千行,而P2分区有200万行数据。同时假若有一列product_id,其上有一个本地分区索引,有以下的SQL:
select * from orders where area=:b1 and produce_id=:b2;
这条SQL因为有area条件,所以会使用分区排除。若是第1 次执行时应用传给b1变量的值正好落在P1分区上,极可能致使SQL采用全表扫描访问,如前面所描述的,致使SQL后续执行时所有使用了全表扫描。
3. 其余缘由,好比表作了相似于MOVE操做以后,索引不可用,对索引进行了更改。固然这种状况是属于维护不当引发的问题,不在本文讨论的范围。
综上所述,SQL语句性能忽然变差,主要是由于绑定变量和统计信息的缘由。注意这里只讨论了忽然变差的状况,而对于因为数据量和业务量的增长性能逐步变差的状况不讨论。
为保持SQL性能或者说是执行计划的稳定性,须要从如下几个方面着手:
1. 规划好优化统计信息的收集策略。对于Oracle 10g来讲,默认的策略可以知足大部分需求,可是默认的收集策略会过多地收集列上的直方图。因为绑定变量与直方图固有的矛盾,为保持性能稳定,对使用绑定变量的列,不收集列上的直方图;对的确须要收集直方图的列,在SQL中该列上的条件就不要用绑定变量。
统计信息收集策略,能够考虑对大部分表,使用系统默认的收集策略,而对于有问题的,能够用DBMS_STATS.LOCK_STATS锁定表的统计信息,避免系统自动收集该表的统计信息,而后编写脚原本定制地收集表的统计信息。脚本中相似以下:
EXEC dbms_stats.unlock_table_stats...
EXEC dbms_stats.gather_table_stats...
EXEC dbms_stats.lock_table_stats...
2. 修改SQL语句,使用HINT,使SQL语句按HINT指定的执行计划进行执行。这须要修改应用,同时须要逐条SQL语句进行,加上测试和发布,时间较长,成本较高,风险也较大。
3. 修改隐含参数” _optim_peek_user_binds”为FALSE,修改这个参数可能会引发性能问题(这里讨论的是稳定性问题)。
4. 使用OUTLINE。对于曾经出现过执行计划忽然变差的SQL语句,可使用OUTLINE来加固其执行计划。在10g中DBMS_OUTLN.CREATE_OUTLINE能够根据已有的执行正常的SQL游标来建立OUTLINE。若是事先对全部频繁执行的核心SQL使用OUTLINE加执拗行计划,将最大可能地避免SQL语句性能忽然变差。
注:DBMS_OUTLN能够经过$ORACLE_HOME/rdbms/admin/dbmsol.sql脚原本安装。
5. 使用SQL Profile。SQL Profile是Oracle 10g以后的新功能,此处再也不介绍,请参考相应的文档。
除此以外,能够调整一些参数避免潜在的问题,好比将"_btree_bitmap_plans"参数设置为FALSE(这个参数请参考互联网上的文章或Oracle文档)。
而在实际工做中,经过使用定制的统计信息收集策略,以及在部分系统上使用OUTLINE,系统基本上不会出现已有的SQL性能忽然变差的状况。固然也有维护人员操做不当引发的SQL性能忽然变差,好比建了某个索引而没有收集统计信息,致使SQL使用了新建的索引,而该索引并不适合于那条SQL;维护人员意外删除了表个索引的统计信息。