ORACLE性能优化之SQL语句优化

版权声明:本文为博主原创文章,未经博主允许不得转载。

目录(?)[+]

操作环境:AIX +11g+PLSQL

包含以下内容:

1.  SQL语句执行过程

2.  优化器及执行计划

3.  合理应用Hints

4.  索引及应用实例

5.   其他优化技术及应用

1.SQL语句执行过程

1.1 SQL语句的执行步骤

1)语法分析,分析语句的语法是否符合规范,衡量语句中各表达式的意义。

2)语义分析,检查语句中涉及的所有数据库对象是否存在,且用户有相应的权限。

3)视图转换,将涉及视图的查询语句转换为相应的对基表查询语句。

4)表达式转换, 将复杂的 SQL 表达式转换为较简单的等效连接表达式。

5)选择优化器,不同的优化器一般产生不同的“执行计划”

6)选择连接方式, ORACLE 主要有三种连接方式,对多表连接ORACLE会选择适当的连接方式。

7)选择连接顺序, 对多表连接 ORACLE 选择哪一对表先连接,选择这两表中哪张表做为基础数据表。

8)选择数据的搜索路径,根据以上条件选择合适的数据搜索路径,比如,是选用全表搜索还是利用索引或是其他的方式。

9)运行“执行计划”

我们可以通过如下语句来查询缓存中的执行计划:

[sql] view plain copy

  1. SELECT t1.*,
  2. ‘t2-->‘,
  3. t2.*
  4. FROM v$sql_plan t1
  5. JOIN v$sql t2
  6. ON t1.address = t2.address
  7. AND t1.hash_value = t2.hash_value
  8. AND t1.child_number = t2.child_number;--缓存中的执行计划。

1.2 典型SELECT语句完整的执行顺序

1)from子句组装来自不同数据源的数据;

2)where子句基于指定的条件对记录行进行筛选;

3)group by子句将数据划分为多个分组;

4)使用聚集函数进行计算;

5)使用having子句筛选分组;

6)计算所有的表达式;

7)计算select的字段;

8)使用order by对结果集进行排序。

1.3 SQL语句执行过程

如下图所示:

说明:

*这是一张SQL语句执行过程图

*执行计划是SQL语句执行过程中必然用到的

*执行计划是优化器(Optimizer)的产物

*两种不同的方式:CBO和RBO

查看优化器设置:

方法一:

[sql] view plain copy

  1. SELECT VALUE FROM v$parameter t WHERE t.name = ‘optimizer_mode‘;

方法二(SQLPLUS下执行):

[sql] view plain copy

  1. showparameter optimizer_mode

*CBO用到了字典中的Statistics,而RBO没有

分析统计信息相关SQL:

[sql] view plain copy

  1. analyze table tablename compute statistics;

[sql] view plain copy

  1. analyze table tablename compute statistics for all indexes

[sql] view plain copy

  1. analyze table tablename delete statistics

2.优化器及执行计划

2.1 SQL优化方法论

*ORACLE10g以后的版本,SQL优化的本质是基于对CBO和执行计划的深刻理解,进入CBO时代,一定要理解执行计划。
*查看执行计划有好多方式,比如使用PL/SQL Developer工具,选中select语句,按F5键就可以显示其执行计划,不过显示的不完全

*最好使用在Oracle官方的sqlplus工具,性能最好,方便直观,下面介绍两种查看执行计划方式(也是最简单的两种方式)

关于执行计划的一些知识:

* Full Table Scans 全表扫描

* Rowid Scans  rowid扫描

* Index Scans 索引扫描

* Index Unique Scans

* Index Range Scans

* Index Range Scans Descending

* Index Skip Scans

* Full Scans

* Fast Full Index Scans(CBO)

* Index Joins

* Bitmap Joins

* Cluster Scans  簇扫描

* Hash Scans   散列扫描

* Sample Table Scans  表取样扫描

²在RBO时代,关于access path,很简单,有index就用,而对于join方法,编程人员一般会通过调整关联表之间的先后顺序来获得比较好的运行结果。有什么缺点呢?

²有了CBO,简单就是两个字-----CBO走的是包办婚姻:你的事交给我办。

ORACLE默认情况下,周一到周五每天晚上10点到第二天早上6点以及整个周末期间会自动收集统计信息

可以查看参数:

[sql] view plain copy

  1. show parameter STATISTICS_LEVEL

²问题:CBO执行计划依赖的statistic不准确(缺失或者太旧),导致在计算执行成本时就会出现偏差,很可能会产生错误的执行计划,怎么办呢?
第一步:重新收集统计信息!
第二部:第一部解决不了的情况下,使用Hints

3.合理应用Hints

3.1Hints

慎用hint,可能会产生严重的后果,比如append会产生锁块,导致并发资源等待等

Hints的分类:

*Hints forOptimization Approaches and Goals(4)

/*+ ALL_ROWS */

/*+ FIRST_ROWS ( n )*/

/*+ CHOOSE */

/*+ RULE */

*Hints for AccessPaths(12)

/*+ FULL ( table ) */

/*+ INDEX ( tableindex) */

/*+ INDEX_ASC ( tableindex) */

/*+ INDEX_COMBINE (table index) */

/*+ INDEX_JOIN (table index) */

/*+ INDEX_DESC (table index) */

/*+ INDEX_FFS ( tableindex) */

/*+ NO_INDEX ( tableindex) */

/*+ AND_EQUAL ( tableindex index) */

*Hints for QueryTransformations(10)

*Hints for JoinOrders(2)

*Hints for JoinOperations(11)

/*+ USE_NL ( table )*/

/*+ USE_MERGE ( table) */

/*+ USE_HASH ( table) */

/*+ LEADING ( table )*/

*Hints for ParallelExecution(5)

*Additional Hints(13)

以下为使用Hints的例子

[sql] view plain copy

  1. create table t_1(owner varchar2(30),table_name varchar2(30));
  2. create table t_2(owner varchar2(30),table_name varchar2(30));
  3. insert into t_1 SELECT owner,table_name FROM dba_tables;
  4. insert into t_2 SELECT owner,view_name  FROM dba_views t;
  5. create index idx_t_1 on t_1(table_name);
  6. create index idx_t_2 on t_2(table_name);
  7. analyze table t_1  compute statistics;
  8. analyze table t_2  compute statistics;
  9. SELECT *
  10. FROM (SELECT * FROM t_1
  11. UNION ALL
  12. SELECT * FROM t_2) aa
  13. WHERE aa.table_name LIKE ‘Z%‘;                 ---- Full Table Scans
  14. SELECT /*+ index(AA.t_1 idx_t_1) index(AA.t_2 idx_t_2)*/ *
  15. FROM (SELECT * FROM t_1
  16. UNION ALL
  17. SELECT * FROM t_2) AA
  18. WHERE AA.table_name LIKE ‘Z%‘;               ---- Index Scans

贴上执行图:

4.索引及应用实例

4.1什么是索引

*Oracle的索引是一种自平衡的B*Tree存储结构,其基本存储单位为数据块,称之为节点,共有三种类型的节点:根(root)节点,分枝(Branch)节点,叶(leaf)节点。

*分枝节点存储{索引值,键值对应下一级节点块地址,lmc指针}

*叶节点存储{索引值及其rowid,当前节点的前后节点的数据块地址}
所有叶节点上的两个指针形成一个双向链表,在这个双向链表上的所有索引值,从小到大排列,而对于倒序desc索引,则是从大到小排列

B*TREE索引图:

4.2索引分类

逻辑上: 
Single column 单列索引
Concatenated 多列索引
Unique 唯一索引
Non-Unique 非唯一索引
Function-based函数索引
Domain 域索引

物理上: 
Partitioned 分区索引
Non-Partitioned 非分区索引
B*tree:
  Normal 正常型B树
  ReverseKey 反转型B树
  Bitmap 位图索引

4.3什么时候使用索引

*如果要检索全表,不必要建索引,因为索引会带来额外的IO操作。

*如果检索的记录数占全部表记录的10%以下可以考虑建索引(大表)。

*表之间的关联字段可以考虑建索引,特别是一张大表和一张小表的关联。

*B*Tree索引适合于大量的增、删、改(OLTP);
     不适合用包含OR操作符的查询;一般不适用NULL判断;
     适合高基数的列(重复值少)

*Bitmap索引适合于决策支持系统OLAP;
    做UPDATE代价比较高;会锁块;
    非常适合OR操作符的查询;
    适合低基数的列(比如,只有Y和N两种值) ;

*Reverse索引反转了b*tree索引码中的字节,是索引条目分配更均匀,多用于并行服务器环境下,用于减少索引叶的竞争。

索引是’双刃剑’,在查询与DML之间寻求平衡

4.4改写SQL使用索引

*普通索引列 a is not null 按逻辑改为a>0或a>‘‘

*like操作改写

*能用union all绝不用union,除非要去重

*in操作虽然简单易懂,但oracle内部会转换为表连接查询,使用in会多一步转换操作,所以建议使用表关联查询

*not in 强烈建议使用not exists或(外连接+判断为空)

*<>(不等于)操作不走索引,推荐a<>0改为(a>0 ora<0)    a<>’’改为a>’’

*提防隐式类型转换, oracle内部处理a=0与a=‘0’是完全不同的,甚至会导致不走索引,这个深有体会,最近一个项目就是这个隐式类型转换出了问题,导致速度

很慢

4.5索引应用

例1.用合适的索引来避免不必要的全表扫

如果要在索引列查询is not null条件,建议列加上is not null约束,默认值约束,

然而确实由于某种原因索引列设计为null,还想通过is null条件走索引,该如何是好呢?请看

[sql] view plain copy

  1. drop table t_tab1;
  2. create table t_tab1 as
  3. SELECT t.owner,
  4. t.object_name,
  5. t.object_type,
  6. t.created,
  7. t.last_ddl_time
  8. FROM dba_objects t;
  9. analyze table t_tab1  compute statistics;
  10. create index idx01_t_tab1 on t_tab1(last_ddl_time);--普通索引
  11. set autotrace trace;
  12. SELECT * FROM t_tab1 t where t.last_ddl_time is null;

执行计划如下图:

如上情况调整为复合索引

[sql] view plain copy

  1. drop index idx01_t_tab1;
  2. create index idx01_t_tab1 on t_tab1(last_ddl_time,1);--加了个常量
  3. set autotrace trace;
  4. SELECT * FROM t_tab1 t where t.last_ddl_time is null;

执行计划如下图:

例2:用合适的函数索引来避免看似无法避免的全表扫描

[sql] view plain copy

  1. drop table t_tab1 purge;
  2. create table t_tab1 as
  3. SELECT t.owner,
  4. t.object_name,
  5. t.object_type,
  6. t.OBJECT_ID,
  7. t.created,
  8. t.last_ddl_time
  9. FROM dba_objects t;
  10. CREATE INDEX IDX01_T_TAB1 ON T_TAB1(object_name);
  11. analyze table t_tab1  compute statistics;
  12. set autot trace
  13. SELECT * FROM t_tab1 t where t.object_name like ‘%20121231‘;

执行计划如下:

改进索引,此处使用反转函数索引,此外经常用到的函数索引还有,instr(),substr()等

[sql] view plain copy

  1. drop index IDX01_T_TAB1;
  2. CREATE INDEX IDX02_T_TAB1 ON T_TAB1(reverse(object_name));
  3. analyze table t_tab1  compute statistics;
  4. SELECT * FROM t_tab1 t where reverse(t.object_name) like reverse(‘%20121231‘);

执行计划如下:

5.其他优化技术及应用

5.1其他优化技术及思路

并行技术,并行执行目标SQL语句,这实际上是以额外的资源消耗来换取执行时间的缩短,很多情况下使用并行是针对某些SQL的唯一优化手段。

使用shell调度或其他调度工具。

SQL语句级别的并行:/*+parallel*/

/*+ parallel(table_name 4)*/

表压缩技术

compress

NOLOGGING

减少日志

Partition技术

分而治之

中间表/临时表事务分解思路

‘大事化小’

求平衡

CPU,Memory很强大,IO存在瓶颈(最普遍的情况)

使用新特性

insertall 啦       使用listagg()比wm_concat()快大概50倍、row_number()等分析函数

软硬件资源合理搭配

黔驴技穷,要求加硬件资源? Boss会对你说,找会计去吧,提前给你开工资 ……

5.2 SQL优化总结

SQL的优化的手段是五花八门、不一而足的,包括但不限于如下措施:

*如果是统计信息不准或是因为CBO计算某些SQL的执行路径(Access Path)的成本所用公式的先天不足而导致的SQL性能问题,
 我们可以通过重新收集统计信息或者手工修改统计信息或者使用Hint来加以解决;

*如果是SQL语句的写法问题,我们可以通过在不更改业务逻辑的情况下改写SQL来加以解决;

*如果是不必要的全表扫描/排序而导致了目标SQL的性能问题,我们可以通过建立合适的索引(包括函数索引、位图索引等)来加以解决;

*如果是表或者索引的不良设计导致的目标SQL的性能问题,我们可以通过重新设计表/索引,重新组织表里的数据来加以解决;

*如果上述调整措施都失效,我们可以考虑用并行来缩短目标SQL的执行时间;

*如果上述调整措施、包括并行都失效,我们还可以在联系实际业务的基础上更改目标SQL的执行逻辑,甚至不执行目标SQL,这是最彻底的优化:)

时间: 2024-08-23 03:38:49

ORACLE性能优化之SQL语句优化的相关文章

数据库性能优化之SQL语句优化

一.问题的提出 在应用系统开发初期,由于开发数据库数据比较少,对于查询SQL语句,复杂视图的的编写等体会不出SQL语句各种写法的性能优劣,但是如果将应用系统提交实际应用后,随着数据库中数据的增加,系统的响应速度就成为目前系统需要解决的最主要的问题之一.系统优化中一个很重要的方面就是SQL语句的优化.对于海量数据,劣质SQL语句和优质SQL语句之间的速度差别可以达到上百倍,可见对于一个系统不是简单地能实现其功能就可,而是要写出高质量的SQL语句,提高系统的可用性. 在多数情况下,Oracle使用索

数据库性能优化之SQL语句优化1

一.问题的提出 在 应用系统开发初期,由于开发数据库数据比较少,对于查询SQL语句,复杂视图的的编写等体会不出SQL语句各种写法的性能优劣,但是如果将应用系统提交实 际应用后,随着数据库中数据的增加,系统的响应速度就成为目前系统需要解决的最主要的问题之一.系统优化中一个很重要的方面就是SQL语句的优化.对于海 量数据,劣质SQL语句和优质SQL语句之间的速度差别可以达到上百倍,可见对于一个系统不是简单地能实现其功能就可,而是要写出高质量的SQL语句,提 高系统的可用性. 在多数情况下,Oracl

数据库性能调优——sql语句优化(转载及整理)

下面是在网上搜集的一些个人认为比较正确的调优方案,如有错误望指出,定虚心改正 (1) 选择最有效率的表名顺序(只在基于规则的优化器中有效): ORACLE 的解析器按照从右到左的顺序处理FROM子句中的表名,FROM子句中写在最后的表(基础表 driving table)将被最先处理,在FROM子句中包含多个表的情况下,你必须选择记录条数最少的表作为基础表.如果有3个以上的表连接查询, 那就需要选择交叉表(intersection table)作为基础表, 交叉表是指那个被其他表所引用的表. (

[转]数据库性能优化之SQL语句优化1

一.问题的提出 在应用系统开发初期,由于开发数据库数据比较少,对于查询SQL语句,复杂视图的的编写等体会不出SQL语句各种写法的性能优劣,但是如果将应用系统提交实际应用后,随着数据库中数据的增加,系统的响应速度就成为目前系统需要解决的最主要的问题之一.系统优化中一个很重要的方面就是SQL语句的优化.对于海量数据,劣质SQL语句和优质SQL语句之间的速度差别可以达到上百倍,可见对于一个系统不是简单地能实现其功能就可,而是要写出高质量的SQL语句,提高系统的可用性. 在多数情况下,Oracle使用索

数据库性能优化之SQL语句优化(下)

(1) 选择最有效率的表名顺序(只在基于规则的优化器中有效): ORACLE 的解析器按照从右到左的顺序处理FROM子句中的表名,FROM子句中写在最后的表(基础表 driving table)将被最先处理,在FROM子句中包含多个表的情况下,你必须选择记录条数最少的表作为基础表.如果有3个以上的表连接查询, 那就需要选择交叉表(intersection table)作为基础表, 交叉表是指那个被其他表所引用的表. (2) WHERE子句中的连接顺序: ORACLE采用自下而上的顺序解析WHER

数据库优化之SQL语句优化-记录

1. 操作符优化 (a) IN 操作符 从Oracle执行的步骤来分析用IN的SQL与不用IN的SQL有以下区别: ORACLE试图将其转换成多个表的连接,如果转换不成功则先执行IN里面的子查询,再查询外层的表记录,如果转换成功则直接采用多个表的连接方式查询.由此可见用IN的SQL至少多了一个转换的过程.一般的SQL都可以转换成功,但对于含有分组统计等方面的SQL就不能转换了. 推荐方案:在业务密集的SQL当中尽量不采用IN操作符,用EXISTS 方案代替. (b) NOT IN操作符 不能应用

mysql索引优化和sql语句优化

一.mysql索引分为btree索引和hash索引. btree索引是二叉树结构 先到索引树上找,再去根据索引到数据里边找数据. hash索引是memory引擎,精准查询非常快,如果查范围内(where>8),会比较慢.因为是无序的,无法使用前缀索引. 2.btree索引 建立索引,通常是经常用到做查询条件,做分组,做排序. 独立索引,单个建索引只能用一个索引,通常用联合索引. 如建了一个(a,b,c)的复合索引,那么实际等于建了(a),(a,b),(a,b,c)三个索引,因为每多一个索引,都会

数据库性能优化之SQL语句优化(上)

一.问题的提出 在应用系统开发初期,由于开发数据库数据比较少,对于查询SQL语句,复杂视图的编写等体会不出SQL语句各种写法的性能优劣,但是如果将应用系统提交实际应用后,随着数据库中数据的增加,系统的响应速度就成为目前系统需要解决的最主要的问题之一. 系统优化中一个很重要的方面就是SQL语句的优化.对于海量数据,劣质SQL语句和优质SQL语句之间的速度差别可以达到上百倍,可见对于一个系统不是简单地能实现其功能就可,而是要写出高质量的SQL语句,提高系统的可用性. 在多数情况下,Oracle使用索

SQL Server优化之SQL语句优化

一切都是为了性能,一切都是为了业务 一.查询的逻辑执行顺序 (1) FROM left_table (3) join_type JOIN right_table (2) ON join_condition (4) WHERE where_condition (5) GROUP BY group_by_list (6) WITH {cube | rollup} (7) HAVING having_condition (8) SELECT (9) DISTINCT (11) top_specific