oracle性能调优-转自《Oracle Database 11g数据库管理艺术》第19章(改进数据库性能:SQL查询优化)
1. 高效的where子句
选择性原则--查询所返回的行数占全部行数的百分比,低百分比对应高选择性,高百分比对应低选择性。
 
例子:select * from employee where no=221342 and city='DALLAS';
no=221342子句需要的IO较少,因为列no是主键,具有非常高的选择性(行只有1个),
优化程序通过查看索引统计数据为这两个列中的每个列确定其选择性,索引数据告诉优化程序在表中有多
少行包含了查询的两个列值中的每个列值。
如果两个列都没有索引,则全表扫描进行检索以回复查询。
如果两个列都有索引,则对no列使用更多的选择性(因此也更有效)索引。
 
某个列有索引,但并不能保证索引总能被使用,如果like '%xxx%' 查询会跳过索引并进行全表扫描。
例如,如果一个表有1000行数据,位于200个数据块中,假定db_file_multiblock_read_count(一次IO扫
描的块数)的值为8,对这样一个表执行全表扫描时,在整个表中会执行25次IO读操作。
如果索引是低选择性的,就会首先读取大多数索引,如果索引有40个页块,并且首先读取了其中90%的叶块
以得到被索引的数据,而且IO读操作已经执行了32次。此外,还要额外的IO操作用来读取表值。
而一个全表扫描只需要25次IO操作,这比使用索引更有效。
 
(1)使用SQL函数
如果在where子句中使用sql函数(如substr、instr、to_date、to_number),oracle优化程序会忽略列上
的索引,如果必须在子句上使用函数,应确保使用的是基于函数的索引。
(2)使用右联结
建议1:使用等值联结(equi join)得到比使用其他方式更有效的查询路径。尽量使用等值联结。
建议2:较早的使用过滤操作可以减少后面要进行联结的行数。即传递给下一步的行较少。
建议3:以产生最小行数的顺序进行联结,作为上个步骤的输出结果。
(3)使用case语句
当需要计算来自同一个表的多个聚集时,不要为每个聚集编写单独的查询。因为每次查询oracle都要读取
整个表。这时可以使用case语句一次读表计算来自该表的多个聚集。
(4)执行高效的子查询
使用in而不是exists时,子查询执行会更好。
如果子查询包括选择性的where子句,oracle建议使用in;
如果夫查询包括选择性的where子句,建议使用exists;
(5)使用where代替having
where子句一开始就限制了被检索的行数,而having子句还需要检索很多不需要的行,这样也会增加额外
的排序和求和操作。
(6)最小化表查找
尽可能少的访问数据,意味着要删除那些为不同列值而对一个表进行重复访问的sql语句。此时应使用多列
更新。
 
2. 使用提示影响执行计划
优化程序是基于规则的,一个好的应用开发人员对应用程序和数据的了解要比CBO更多。可以为优化程序提
供提示以取代CBO的执行计划,例如你知道某个索引效果比其他索引更好,那么你可以强制使用这个索引。
常用提示:
(1)ALL_ROWS:对吞吐量进行优化(最小成本优化),但不优化语句的响应事件
(2)FIRST_ROWS(n):要求oracle快速返回前n行数据。目的是较低的响应时间
(3)FULL:要求全表扫描并忽略可能存在的索引。如果你确定全表扫描效率高时可以这么做。
(4)ORDERED:强制在查询中表的联结次序
(5)INDEX:强制使用一个索引扫描,即优化程序可能出于某种原因忽略了该索引而执行了全表扫描
(6)INDEX_FFS:索引快速全扫描(INDEX_FFS)提示强制对索引进行一个快速全扫表。
 
3. 选择最佳的联结方式
(1)避免笛卡儿积联结
(2)嵌套查询:要对小的数据子集(少于10000行)进行联结,如果优化程序使用的是散列联结或全表
     扫描,可以使用提示强制优化程序使用嵌套联结方法: select /*+ USE_NL(TableA, TableB) */
(3)散列联结:联结会产生大的数据子集或表中的很大一部分都要被联结而优化程序并不打算使用散列
     联结,此时可以使用散列联结提示:select /* USE_HASH */
(4)合并联结:如果联结中的表是被一个不等式条件(非等值联结)所联结,那么合并联结最理想。
     select /*+ USE_MERGE(TableA, TableB) */
 
4. 使用位图联结索引
位图联结索引(BJI)预先在一个索引中存储了两个表之间的联结结果,从而取消了进行代价高昂的运行时
联结操作。
 
5. 选择最佳的联结次序
在联结中,驱动表是紧跟在where子句后面的第一个表。
联结中的驱动表应当包含能够去除大多数行的过滤条件。
选择的联结次序的依据是可以让最少数目的行与其他表进行联结。
也就是说如果你联结了3个表,那么具有更多限制的过滤条件的表应当在其他两个表之前被联结。
 
6. 索引策略
索引是一种数据结构(如mysql是b+ tree),它取得一个表(键)的一个或多个列的值,并返回具有该
列值的所有行(或者在一个行中所要求的列)。
提高效率的原因:只查找必要的行而不需要对一个表的所有行进行扫描,即与全表扫描比磁盘IO少。
(1)什么时候使用索引?
当表的数据达到一定的数量级,正常查询较慢,且只有在你认为你的查询将只会选择表中的一小部分数据时,
才需要对表进行索引。如果查询所检索的行数超过表总行数的10%或15%,就不需要索引(从索引中获取行
的rowid-即磁盘上的逻辑地址)。如果在一个表中强调行的唯一性,那么可以对这个表使用一个主索引(非空且唯一)。
通常一个表的主索引有1个,更多的是次索引,过多的次索引会增加维护成本,除了需要额外的磁盘空间,
因为对表的insert和update也需要同时对索引进行更新,所以如果该表包含了大量的insert和delete,太多的索引
反而有害。
(2)对什么进行索引
尽可能使用较少的索引来满足性能需求。
建议1:对高选择性的列进行索引,高选择性意味着具有同样值得行很少
建议2:对所有重复得外键进行索引
建议3:对所有谓词列进行索引
建议4:对在表联结中使用得列进行索引
建议5:尽量避免对由长字符串构成得列进行索引,除非你正在使用oracle text特性
建议6:无论什么情况下,尽量使用index-only计划,创建必要得组合索引
建议7:对order by和group by操作以及union或distinct这样的分类操作中经常涉及的列使用次索引。
(3)使用合适的索引类型
B-树索引(或B*树)是默认的或标准的oracle索引,在一个一般的OLTP应用程序中,几乎所有索引都是该类型。
方案1:位图索引,对于具有低基数(low cardinality)的列数据使用最理想。
方案2:索引组织表(IOT),数据行按主键顺序被保存,节省空间,加快访问速度
方案3:拼接索引(或组合索引),包含一个以上列的索引,如果被拼接的索引包含where列中所有列,就会免除
       查找表的麻烦,从而减少IO次数。但需要对索引中列的舒徐特别注意。
方案4:基于函数的索引,包含一个oracle函数或表达式进行转换后的列。当使用函数或表达式创建的索引哎查询
       的where子句中使用时,可以直接从索引中快速返回经过函数或表达式计算过的值,而不需要每次重新计算
方案5:反向键索引,如果一个带有大量insert操作的数据库存在性能问题可以考虑该索引,在处理一次增加的列
       时,反向键可以提供一种有效的方式使得索引值分布均匀,从而改进性能。
方案6:分区索引策略,被分区的索引可以是局部的,也可以是全局的
 
7. 监控索引的使用
一个表可能有几个索引,但不能保证这些索引在查询种都能被使用,如果没有使用,不妨删除它,因为索引会减慢
DML操作的速度,可以使用v$object_usage视图收集索引使用信息。
 
8. 使用相似的SQL语句
除了保留SGA的共享池区的使用之外,重用已经分析过的语句还会得到性能上的改进,但是,其前提是SQL语句必须
在各个方面都完全相同,包括空格以及其他部分。
 
9. 通过内嵌函数减少sql开销
 
10. 使用绑定变量
查询处理的分析阶段会消耗资源,因此理想情况下是只分析一次,然后重复执行的操作使用同一个被分析的语句版本。
分析是比执行语句花费资源更多的操作。
应当在sql语句中使用绑定变量来代替字面值以减少数据库中分析的次数。
 
11. 避免不恰当地使用视图
视图有几个好处,但并不包括提高性能。
因为视图是一个sql查询,如果你想再次查询视图,它会再执行这个实例化过程。
如果你的查询对视图做了联结,那么执行这个查询将耗费大量的事件。
 
12. 避免不必要的全表扫描
即使已经做了索引,全表扫描还是会发生。
避免在谓词中使用不等于和大于等于,因为使用他们也可能跳过索引。
 
13. 找DBA帮忙,下面的主要是DBA可以做到的一些帮助
(1)使用分区表,主要针对大表,上千万行的记录,oracle11g有5种分区模式可以供选择。
(2)使用压缩技术,即表压缩,减少表的空间需求,增强查询性能。
(3)使用实体化视图,改进响应时间。
(4)使用存储大纲保证CBO的稳定性。
 
14. SQL性能调优工具
(1)explain plan
(2)autotrace
(3)sql trace
(4)tkprof
(5)DBMS_MONITOR程序包(端到端追踪)
 
15. 使用v$sql视图查询低效sql,还可以使用其他的字典视图如v$sql_plan、v$sql_plan_statustics
(1)对前5个花费最多cpu时间和最多占用时间的查询进行排序
select sql_text, executions,
       round(elapsed_time/1000000, 2) elapsed_seconds,
       round(cpu_time/1000000, 2) cpu_seconds
  from (select * from v$sql order by elapsed_time desc)
 where ROWNUM<6;
(2)显示较高的磁盘读取和较高的逻辑读取
select sql_text, executions, buffer_gets, disk_reads
  from v$sql
 where buffer_gets>100000 or disk_reads>100000
 order by buffer_gets + 100 * disk_reads desc ;
 
16. 使用sql tuning advisor来改进低效的sql语句
 
17. 使用结果高速缓存-cache