有客户遇到SQL性能不稳定 突然变差导致系统性能出现严重问题的情况 对于大型的系统来说 SQL性能不稳定 有时突然变差 这是常常遇到的问题 这也是一些DBA的挑战
对于使用Oracle数据库的应用系统 有时会出现运行得好好的SQL 性能突然变差 特别是对于OLTP类型系统执行频繁的核心SQL 如果出现性能问题 通常会影响整个数据库的性能 进而影响整个系统的正常运行 对于个别的SQL 比如较少使用的查询报表之类的SQL 如果出现问题 通常只影响少部分功能模块 而不会影响整个系统
那么应该怎么样保持SQL性能的稳定性?
SQL的性能变差 通常是在SQL语句重新进行了解析 解析时使用了错误的执行计划出现的 下列情况是SQL会重新解析的原因
SQL语句没有使用绑定变量 这样SQL每次执行都要解析
SQL长时间没有执行 被刷出SHARED POOL 再次执行时需要重新解析
在SQL引用的对象(表 视图等)上执行了DDL *** 作 甚至是结构发生了变化 比如建了一个索引
对SQL引用的对象进行了权限更改
重新分析(收集统计信息)了SQL引用的表和索引 或者表和索引统计信息被删除
修改了与性能相关的部分参数
刷新了共享池
当然重启数据库也会使所有SQL全部重新解析
SQL重新解析后 跟以前相比 性能突然变差 通常是下列原因
表和索引的优化统计信息被删除 或者重新收集后统计信息不准确 重新收集统计信息通常是由于收集策略(方法)不正确引起 比如对分区表使用 *** yze命令而不是用dbms_stats包 收集统计信息时采样比例过小等等 Oracle优化器严重依赖于统计信息 如果统计信息有问题 则很容易导致SQL不能使用正确的执行计划
SQL绑定变量窥探(bind peeking) 同时绑定变量对应的列上有直方图 或者绑定变量的值变化范围过大 分区数据分布极不均匀
) 绑定变量的列上有直方图
假如表orders存储所有的订单 state列有 种不同的值 表示未处理 表示处理成功完成 表示处理失败 State列上有一个索引 表中绝大部分数据的state列为 和 占少数 有下面的SQL
select * from orders where state=:b
这里:b 是变量 在大多数情况下这个值为 则应该使用索引 但是如果SQL被重新解析 而第一次执行时应用传给变量b 值为 则不会使用索引 采用全表扫描的方式来访问表 对于绑定变量的SQL 只在第一次执行时才会进行绑定变量窥探 并以此确定执行计划 该SQL后续执行时全部按这个执行计划 这样在后续执行时 b 变量传入的值为 的时候 仍然是第一次执行时产生的执行计划 即使用的是全表扫描 这样会导致性能很差
) 绑定变量的值变化范围过大
同样假如orders表有一列created_date表示一笔订单的下单时间 orders表里面存储了最近 年的数据 有如下的SQL
Select * from orders where created_date >=:b
假如大多数情况下 应用传入的b 变量值为最近几天内的日期值 那么SQL使用的是created_date列上的索引 而如果b 变量值为 个月之前的一个值 那么就会使用全表扫描 与上面描述的直方图引起的问题一样 如果SQL第 次执行时传入的变量值引起的是全表扫描 那么将该SQL后续执行时都使用了全表扫描 从而影响了性能
) 分区数据量不均匀
对于范围和列表分区 可能存在各个分区之间数据量极不均匀的情况下 比如分区表orders按地区area进行了分区 P 分区只有几千行 而P 分区有 万行数据 同时假如有一列product_id 其上有一个本地分区索引 有如下的SQL
select * from orders where area=:b and product_id =:b
这条SQL由于有area条件 因此会使用分区排除 如果第 次执行时应用传给b 变量的值正好落在P 分区上 很可能导致SQL采用全表扫描访问 如前面所描述的 导致SQL后续执行时全部使用了全表扫描
其他原因 比如表做了类似于MOVE *** 作之后 索引不可用 对索引进行了更改 当然这种情况是属于维护不当引起的问题 不在本文讨论的范围
综上所述 SQL语句性能突然变差 主要是因为绑定变量和统计信息的原因 注意这里只讨论了突然变差的情况 而对于由于数据量和业务量的增加性能逐步变差的情况不讨论
为保持SQL性能或者说是执行计划的稳定性 需要从以下几个方面着手
规划好优化统计信息的收集策略 对于Oracle g来说 默认的策略能够满足大部分需求 但是默认的收集策略会过多地收集列上的直方图 由于绑定变量与直方图固有的矛盾 为保持性能稳定 对使用绑定变量的列 不收集列上的直方图 对的确需要收集直方图的列 在SQL中该列上的条件就不要用绑定变量 统计信息收集策略 可以考虑对大部分表 使用系统默认的收集策略 而对于有问题的 可以用DBMS_STATS LOCK_STATS锁定表的统计信息 避免系统自动收集该表的统计信息 然后编写脚本来定制地收集表的统计信息 脚本中类似如下
exec dbms_stats unlock_table_stats…
exec dbms_stats gather_table_stats…
exec dbms_stats lock_table_stats…
修改SQL语句 使用HINT 使SQL语句按HINT指定的执行计划进行执行 这需要修改应用 同时需要逐条SQL语句进行 加上测试和发布 时间较长 成本较高 风险也较大
修改隐含参数 _optim_peek_user_binds 为FALSE 修改这个参数可能会引起性能问题(这里讨论的是稳定性问题)
使用OUTLINE 对于曾经出现过执行计划突然变差的SQL语句 可以使用OUTLINE来加固其执行计划 在 g中DBMS_OUTLN CREATE_OUTLINE可以根据已有的执行正常的SQL游标来创建OUTLINE 如果事先对所有频繁执行的核心SQL使用OUTLINE加固执行计划 将最大可能地避免SQL语句性能突然变差
注 DBMS_OUTLN可以通过$ORACLE_HOME/rdbms/admin/dbmsol sql脚本来安装
使用SQL Profile SQL Profile是Oracle g之后的新功能 此处不再介绍 请参考相应的文档
除此之外 可以调整一些参数避免潜在的问题 比如将 _btree_bitmap_plans 参数设置为FALSE(这个参数请参考互联网上的文章或Oracle文档)
lishixinzhi/Article/program/Oracle/201311/180541、1、调整数据结构的设计。这一部分在开发信息系统之前完成,程序员需要考虑是否使用ORACLE数据库的分区功能,对于经常访问的数据库表是否需要建立索引等。 \x0d\x0a\x0d\x0a2、2、调整应用程序结构设计。这一部分也是在开发信息系统之前完成,程序员在这一步需要考虑应用程序使用什么样的体系结构,是使用传统的Client/Server两层体系结构,还是使用Browser/Web/Database的三层体系结构。不同的应用程序体系结构要求的数据库资源是不同的。 \x0d\x0a\x0d\x0a3、3、调整数据库SQL语句。应用程序的执行最终将归结为数据库中的SQL语句执行,因此SQL语句的执行效率最终决定了ORACLE数据库的性能。ORACLE公司推荐使用ORACLE语句优化器(Oracle Optimizer)和行锁管理器(row-level manager)来调整优化SQL语句。 \x0d\x0a\x0d\x0a4、4、调整服务器内存分配。内存分配是在信息系统运行过程中优化配置的,数据库管理员可以根据数据库运行状况调整数据库系统全局区(SGA区)的数据缓冲区、日志缓冲区和共享池的大小;还可以调整程序全局区(PGA区)的大小。需要注意的是,SGA区不是越大越好,SGA区过大会占用 *** 作系统使用的内存而引起虚拟内存的页面交换,这样反而会降低系统。 \x0d\x0a\x0d\x0a5、5、调整硬盘I/O,这一步是在信息系统开发之前完成的。数据库管理员可以将组成同一个表空间的数据文件放在不同的硬盘上,做到硬盘之间I/O负载均衡。 \x0d\x0a\x0d\x0a6、6、调整 *** 作系统参数,例如:运行在UNIX *** 作系统上的ORACLE数据库,可以调整UNIX数据缓冲池的大小,每个进程所能使用的内存大小等参数。 \x0d\x0a\x0d\x0a实际上,上述数据库优化措施之间是相互联系的。ORACLE数据库性能恶化表现基本上都是用户响应时间比较长,需要用户长时间的等待。但性能恶化的原因却是多种多样的,有时是多个因素共同造成了性能恶化的结果,这就需要数据库管理员有比较全面的计算机知识,能够敏感地察觉到影响数据库性能的主要原因所在。另外,良好的数据库管理工具对于优化数据库性能也是很重要的。 \x0d\x0a\x0d\x0aORACLE数据库性能优化工具 \x0d\x0a\x0d\x0a常用的数据库性能优化工具有: \x0d\x0a\x0d\x0a1、1、ORACLE数据库在线数据字典,ORACLE在线数据字典能够反映出ORACLE动态运行情况,对于调整数据库性能是很有帮助的。 \x0d\x0a\x0d\x0a2、2、 *** 作系统工具,例如UNIX *** 作系统的vmstat,iostat等命令可以查看到系统系统级内存和硬盘I/O的使用情况,这些工具对于管理员弄清出系统瓶颈出现在什么地方有时候很有用。 \x0d\x0a\x0d\x0a3、3、SQL语言跟踪工具(SQL TRACE FACILITY),SQL语言跟踪工具可以记录SQL语句的执行情况,管理员可以使用虚拟表来调整实例,使用SQL语句跟踪文件调整应用程序性能。SQL语言跟踪工具将结果输出成一个 *** 作系统的文件,管理员可以使用TKPROF工具查看这些文件。 \x0d\x0a\x0d\x0a4、4、ORACLE Enterprise Manager(OEM),这是一个图形的用户管理界面,用户可以使用它方便地进行数据库管理而不必记住复杂的ORACLE数据库管理的命令。 \x0d\x0a\x0d\x0a5、5、EXPLAIN PLAN——SQL语言优化命令,使用这个命令可以帮助程序员写出高效的SQL语言。 \x0d\x0a\x0d\x0aORACLE数据库的系统性能评估 \x0d\x0a\x0d\x0a信息系统的类型不同,需要关注的数据库参数也是不同的。数据库管理员需要根据自己的信息系统的类型着重考虑不同的数据库参数。 \x0d\x0a\x0d\x0a1、1、在线事务处理信息系统(OLTP),这种类型的信息系统一般需要有大量的Insert、Update *** 作,典型的系统包括民航机票发售系统、银行储蓄系统等。OLTP系统需要保证数据库的并发性、可靠性和最终用户的速度,这类系统使用的ORACLE数据库需要主要考虑下述参数: \x0d\x0a\x0d\x0al l 数据库回滚段是否足够? \x0d\x0a\x0d\x0al l 是否需要建立ORACLE数据库索引、聚集、散列? \x0d\x0a\x0d\x0al l 系统全局区(SGA)大小是否足够? \x0d\x0a\x0d\x0al l SQL语句是否高效? \x0d\x0a\x0d\x0a2、2、数据仓库系统(Data Warehousing),这种信息系统的主要任务是从ORACLE的海量数据中进行查询,得到数据之间的某些规律。数据库管理员需要为这种类型的ORACLE数据库着重考虑下述参数: \x0d\x0a\x0d\x0al l 是否采用B*-索引或者bitmap索引? \x0d\x0a\x0d\x0al l 是否采用并行SQL查询以提高查询效率? \x0d\x0a\x0d\x0al l 是否采用PL/SQL函数编写存储过程? \x0d\x0a\x0d\x0al l 有必要的话,需要建立并行数据库提高数据库的查询效率 \x0d\x0a\x0d\x0aSQL语句的调整原则 \x0d\x0a\x0d\x0aSQL语言是一种灵活的语言,相同的功能可以使用不同的语句来实现,但是语句的执行效率是很不相同的。程序员可以使用EXPLAIN PLAN语句来比较各种实现方案,并选出最优的实现方案。总得来讲,程序员写SQL语句需要满足考虑如下规则: \x0d\x0a\x0d\x0a1、1、尽量使用索引。试比较下面两条SQL语句: \x0d\x0a\x0d\x0a语句A:SELECT dname, deptno FROM dept WHERE deptno NOT IN \x0d\x0a\x0d\x0a(SELECT deptno FROM emp)\x0d\x0a\x0d\x0a语句B:SELECT dname, deptno FROM dept WHERE NOT EXISTS \x0d\x0a\x0d\x0a(SELECT deptno FROM emp WHERE dept.deptno = emp.deptno)\x0d\x0a\x0d\x0a这两条查询语句实现的结果是相同的,但是执行语句A的时候,ORACLE会对整个emp表进行扫描,没有使用建立在emp表上的deptno索引,执行语句B的时候,由于在子查询中使用了联合查询,ORACLE只是对emp表进行的部分数据扫描,并利用了deptno列的索引,所以语句B的效率要比语句A的效率高一些。 \x0d\x0a\x0d\x0a2、2、选择联合查询的联合次序。考虑下面的例子: \x0d\x0a\x0d\x0aSELECT stuff FROM taba a, tabb b, tabc c \x0d\x0a\x0d\x0aWHERE a.acol between :alow and :ahigh \x0d\x0a\x0d\x0aAND b.bcol between :blow and :bhigh \x0d\x0a\x0d\x0aAND c.ccol between :clow and :chigh \x0d\x0a\x0d\x0aAND a.key1 = b.key1 \x0d\x0a\x0d\x0aAMD a.key2 = c.key2\x0d\x0a\x0d\x0a这个SQL例子中,程序员首先需要选择要查询的主表,因为主表要进行整个表数据的扫描,所以主表应该数据量最小,所以例子中表A的acol列的范围应该比表B和表C相应列的范围小。 \x0d\x0a\x0d\x0a3、3、在子查询中慎重使用IN或者NOT IN语句,使用where (NOT) exists的效果要好的多。 \x0d\x0a\x0d\x0a4、4、慎重使用视图的联合查询,尤其是比较复杂的视图之间的联合查询。一般对视图的查询最好都分解为对数据表的直接查询效果要好一些。 \x0d\x0a\x0d\x0a5、5、可以在参数文件中设置SHARED_POOL_RESERVED_SIZE参数,这个参数在SGA共享池中保留一个连续的内存空间,连续的内存空间有益于存放大的SQL程序包。 \x0d\x0a\x0d\x0a6、6、ORACLE公司提供的DBMS_SHARED_POOL程序可以帮助程序员将某些经常使用的存储过程“钉”在SQL区中而不被换出内存,程序员对于经常使用并且占用内存很多的存储过程“钉”到内存中有利于提高最终用户的响应时间。 \x0d\x0a\x0d\x0aCPU参数的调整 \x0d\x0a\x0d\x0aCPU是服务器的一项重要资源,服务器良好的工作状态是在工作高峰时CPU的使用率在90%以上。如果空闲时间CPU使用率就在90%以上,说明服务器缺乏CPU资源,如果工作高峰时CPU使用率仍然很低,说明服务器CPU资源还比较富余。 \x0d\x0a\x0d\x0a使用 *** 作相同命令可以看到CPU的使用情况,一般UNIX *** 作系统的服务器,可以使用sar _u命令查看CPU的使用率,NT *** 作系统的服务器,可以使用NT的性能管理器来查看CPU的使用率。 \x0d\x0a\x0d\x0a数据库管理员可以通过查看v$sysstat数据字典中“CPU used by this session”统计项得知ORACLE数据库使用的CPU时间,查看“OS User level CPU time”统计项得知 *** 作系统用户态下的CPU时间,查看“OS System call CPU time”统计项得知 *** 作系统系统态下的CPU时间, *** 作系统总的CPU时间就是用户态和系统态时间之和,如果ORACLE数据库使用的CPU时间占 *** 作系统总的CPU时间90%以上,说明服务器CPU基本上被ORACLE数据库使用着,这是合理,反之,说明服务器CPU被其它程序占用过多,ORACLE数据库无法得到更多的CPU时间。 \x0d\x0a\x0d\x0a数据库管理员还可以通过查看v$sesstat数据字典来获得当前连接ORACLE数据库各个会话占用的CPU时间,从而得知什么会话耗用服务器CPU比较多。 \x0d\x0a\x0d\x0a出现CPU资源不足的情况是很多的:SQL语句的重解析、低效率的SQL语句、锁冲突都会引起CPU资源不足。 \x0d\x0a\x0d\x0a1、数据库管理员可以执行下述语句来查看SQL语句的解析情况: \x0d\x0a\x0d\x0aSELECT * FROM V$SYSSTAT \x0d\x0a\x0d\x0aWHERE NAME IN \x0d\x0a\x0d\x0a('parse time cpu', 'parse time elapsed', 'parse count (hard)')\x0d\x0a\x0d\x0a这里parse time cpu是系统服务时间,parse time elapsed是响应时间,用户等待时间 \x0d\x0a\x0d\x0awaite time = parse time elapsed _ parse time cpu \x0d\x0a\x0d\x0a由此可以得到用户SQL语句平均解析等待时间=waite time / parse count。这个平均等待时间应该接近于0,如果平均解析等待时间过长,数据库管理员可以通过下述语句 \x0d\x0a\x0d\x0aSELECT SQL_TEXT, PARSE_CALLS, EXECUTIONS FROM V$SQLAREA \x0d\x0a\x0d\x0aORDER BY PARSE_CALLS\x0d\x0a\x0d\x0a来发现是什么SQL语句解析效率比较低。程序员可以优化这些语句,或者增加ORACLE参数SESSION_CACHED_CURSORS的值。 \x0d\x0a\x0d\x0a2、数据库管理员还可以通过下述语句: \x0d\x0a\x0d\x0aSELECT BUFFER_GETS, EXECUTIONS, SQL_TEXT FROM V$SQLAREA\x0d\x0a\x0d\x0a查看低效率的SQL语句,优化这些语句也有助于提高CPU的利用率。 \x0d\x0a\x0d\x0a3、3、数据库管理员可以通过v$system_event数据字典中的“latch free”统计项查看ORACLE数据库的冲突情况,如果没有冲突的话,latch free查询出来没有结果。如果冲突太大的话,数据库管理员可以降低spin_count参数值,来消除高的CPU使用率。 \x0d\x0a\x0d\x0a内存参数的调整 \x0d\x0a\x0d\x0a内存参数的调整主要是指ORACLE数据库的系统全局区(SGA)的调整。SGA主要由三部分构成:共享池、数据缓冲区、日志缓冲区。 \x0d\x0a\x0d\x0a1、 1、 共享池由两部分构成:共享SQL区和数据字典缓冲区,共享SQL区是存放用户SQL命令的区域,数据字典缓冲区存放数据库运行的动态信息。数据库管理员通过执行下述语句: \x0d\x0a\x0d\x0aselect (sum(pins - reloads)) / sum(pins) "Lib Cache" from v$librarycache\x0d\x0a\x0d\x0a来查看共享SQL区的使用率。这个使用率应该在90%以上,否则需要增加共享池的大小。数据库管理员还可以执行下述语句: \x0d\x0a\x0d\x0aselect (sum(gets - getmisses - usage - fixed)) / sum(gets) "Row Cache" from v$rowcache\x0d\x0a\x0d\x0a查看数据字典缓冲区的使用率,这个使用率也应该在90%以上,否则需要增加共享池的大小。 \x0d\x0a\x0d\x0a2、 2、 数据缓冲区。数据库管理员可以通过下述语句: \x0d\x0a\x0d\x0aSELECT name, value FROM v$sysstat WHERE name IN ('db block gets', 'consistent gets','physical reads')\x0d\x0a\x0d\x0a来查看数据库数据缓冲区的使用情况。查询出来的结果可以计算出来数据缓冲区的使用命中率=1 - ( physical reads / (db block gets + consistent gets) )。 \x0d\x0a\x0d\x0a这个命中率应该在90%以上,否则需要增加数据缓冲区的大小。 \x0d\x0a\x0d\x0a3、 3、 日志缓冲区。数据库管理员可以通过执行下述语句: \x0d\x0a\x0d\x0aselect name,value from v$sysstat where name in ('redo entries','redo log space requests')查看日志缓冲区的使用情况。查询出的结果可以计算出日志缓冲区的申请失败率: \x0d\x0a\x0d\x0a申请失败率=requests/entries,申请失败率应该接近于0,否则说明日志缓冲区开设太小,需要增加ORACLE数据库的日志缓冲区。
进行SQL性能优化的方法:
1、SQL语句不要写的太复杂。一个SQL语句要尽量简单,不要嵌套太多层。
2、使用『临时表』缓存中间结果。简化SQL语句的重要方法就是采用临时表暂存中间结果,这样可以避免程序中多次扫描主表,也大大减少了阻塞,提高了并发性能。
3、使用like的时候要注意是否会导致全表扫,有的时候会需要进行一些模糊查询例如:select id from table where username like ‘%hollis%’关键词%hollis%,由于hollis前面用到了“%”,因此该查询会使用全表扫描,除非必要,否则不要在关键词前加%。
4、尽量避免使用!=或<> *** 作符。在where语句中使用!=或<>,引擎将放弃使用索引而进行全表扫描。
5、尽量避免使用 or 来连接条件;在 where 子句中使用 or 来连接条件,引擎将放弃使用索引而进行全表扫描。可以使用
select id from t where num=10
union all
select id from t where num=20
替代
select id from t where num=10 or num=20
6、尽量避免使用in和not in:在 where 子句中使用 in和not in,引擎将放弃使用索引而进行全表扫描。可以使用
select id from t where num between 10 and 20
替代
select id from t where num in (10,20)
7、可以考虑强制查询使用索引
select * from table force index(PRI) limit 2(强制使用主键)
select * from table force index(hollis_index) limit 2(强制使用索引"hollis_index")
select * from table force index(PRI,hollis_index) limit 2(强制使用索引"PRI和hollis_index")
8、尽量避免使用表达式、函数等 *** 作作为查询条件;尽量避免大事务 *** 作,提高系统并发能力。尽量避免使用游标;任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。
9、尽可能的使用 varchar/nvarchar 代替 char/nchar。尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。
10、索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率、并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)