Oracle 全表扫描及其执行计划(full table scan)

Oracle 全表扫描及其执行计划(full table scan)

分类: Oracle 性能优化 1300人阅读 评论(1) 收藏 举报

    全表扫描是Oracle访问数据库表是较为常见的访问方式之一。很多朋友一看到SQL语句执行计划中的全表扫描,就要考虑对其进行修理一番。全表扫描的存在,的确存在可能优化的余地。但事实上很多时候全表扫描也并非是最低效的,完全要看不同的情形与场合,任一方式都是有利有弊的,也就是具体情况要具体分析。本文描述了什么是全表扫描以及何时发生全表扫描,何时全表扫描才低效。
  本文涉及到的相关链接:
     高水位线和全表扫描
     启用 AUTOTRACE 功能
     Oracle 测试常用表BIG_TABLE
     Oracle db_file_mulitblock_read_count参数
  
1、什么是全表扫描?
    全表扫描就是扫表表中所有的行,实际上是扫描表中所有的数据块,因为Oracle中最小的存储单位是Oracle block。
    扫描所有的数据块就包括高水位线以内的数据块,即使是空数据块在没有被释放的情形下也会被扫描而导致I/O增加。
    在全表扫描期间,通常情况下,表上这些相邻的数据块被按顺序(sequentially)的方式访问以使得一次I/O可以读取多个数据块。
    一次读取更多的数据块有助于全表扫描使用更少的I/O,对于可读取的数据块被限制于参数DB_FILE_MULTIBLOCK_READ_COUNT。

 

2、何时发生全表扫描?
    a、表上的索引失效或无法被使用的情形(如对谓词使用函数、计算、NULL值、不等运算符、类型转换)
    b、查询条件返回了整个表的大部分数据                 
    c、使用了并行方式访问表
    d、使用full 提示
    e、统计信息缺失时使得Oracle认为全表扫描比索引扫描更高效    
    f、表上的数据块小于DB_FILE_MULTIBLOCK_READ_COUNT值的情形可能产生全表扫描

 

3、演示全表扫描的情形

[sql] view plain copy print ?
  1. a、准备演示环境  
  2. scott@ORA11G> select * from v$version where rownum<2;  
  3.   
  4. BANNER  
  5. --------------------------------------------------------------------------------   
  6. Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production  
  7.   
  8. --创建表t   
  9. scott@ORA11G> CREATE TABLE t   
  10.   2  AS  
  11.   3  SELECT rownum AS n, rpad('*',100,'*'AS pad   
  12.   4  FROM dual  
  13.   5  CONNECT BY level <= 1000;  
  14.   
  15. Table created.  
  16.   
  17. --添加索引   
  18. scott@ORA11G> create unique index t_pk on t(n);  
  19.   
  20. Index created.  
  21.   
  22. scott@ORA11G> alter table t add constraint t_pk primary key(n) using index t_pk;  
  23.   
  24. Table altered.  
  25.   
  26. --收集统计信息   
  27. scott@ORA11G> execute dbms_stats.gather_table_stats('SCOTT','T',cascade=>true);  
  28.   
  29. PL/SQL procedure successfully completed.  
  30.   
  31. scott@ORA11G> set autot trace exp;  
  32. scott@ORA11G> select count(*) from t;   --->count(*)的时候使用了索引快速扫描   
  33.   
  34. Execution Plan  
  35. ----------------------------------------------------------   
  36. Plan hash value: 454320086  
  37. ----------------------------------------------------------------------   
  38. | Id  | Operation             | Name | Rows  | Cost (%CPU)| Time     |  
  39. ----------------------------------------------------------------------   
  40. |   0 | SELECT STATEMENT      |      |     1 |     2   (0)| 00:00:01 |  
  41. |   1 |  SORT AGGREGATE       |      |     1 |            |          |  
  42. |   2 |   INDEX FAST FULL SCAN| T_PK |  1000 |     2   (0)| 00:00:01 |  
  43. ----------------------------------------------------------------------   
  44.   
  45. scott@ORA11G> set autot off;  
  46. scott@ORA11G> alter table t move;  --->进行move table   
  47.   
  48. Table altered.  
  49.   
  50. -->move 之后索引失效,如下所示   
  51. scott@ORA11G> @idx_info             
  52. Enter value for owner: scott  
  53. Enter value for table_name: t  
  54.   
  55. Table Name    INDEX_NAME     CL_NAM               CL_POS STATUS   IDX_TYP         DSCD  
  56. ------------- -------------- -------------------- ------ -------- --------------- ----   
  57. T             T_PK           N                         1 UNUSABLE NORMAL          ASC  
  58.   
  59.   
  60. b、索引失效导致全表扫描  
  61. scott@ORA11G> set autot trace exp;  
  62. scott@ORA11G> select count(*) from t;    
  63.   
  64. Execution Plan  
  65. ----------------------------------------------------------   
  66. Plan hash value: 2966233522  
  67. -------------------------------------------------------------------   
  68. | Id  | Operation          | Name | Rows  | Cost (%CPU)| Time     |  
  69. -------------------------------------------------------------------   
  70. |   0 | SELECT STATEMENT   |      |     1 |     7   (0)| 00:00:01 |  
  71. |   1 |  SORT AGGREGATE    |      |     1 |            |          |  
  72. |   2 |   TABLE ACCESS FULL| T    |  1000 |     7   (0)| 00:00:01 |  
  73. -------------------------------------------------------------------   
  74.   
  75. scott@ORA11G> set autot off;  
  76. scott@ORA11G> alter index t_pk rebuild;   -->重建索引   
  77.   
  78. Index altered.  
  79.   
  80. scott@ORA11G> @idx_info  
  81. Enter value for owner: scott  
  82. Enter value for table_name: t  
  83.   
  84. Table Name     INDEX_NAME       CL_NAM               CL_POS STATUS   IDX_TYP         DSCD  
  85. -------------- ---------------- -------------------- ------ -------- --------------- ----   
  86. T              T_PK             N                         1 VALID    NORMAL          ASC  
  87.   
  88.   
  89. c、返回了整个表的大部分数据使用了全表扫描  
  90. scott@ORA11G> select count(pad) from t where n<=990;  
  91.   
  92. Execution Plan  
  93. ----------------------------------------------------------   
  94. Plan hash value: 2966233522  
  95. ---------------------------------------------------------------------------   
  96. | Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |  
  97. ---------------------------------------------------------------------------   
  98. |   0 | SELECT STATEMENT   |      |     1 |   105 |     7   (0)| 00:00:01 |  
  99. |   1 |  SORT AGGREGATE    |      |     1 |   105 |            |          |  
  100. |*  2 |   TABLE ACCESS FULL| T    |   991 |   101K|     7   (0)| 00:00:01 |  
  101. ---------------------------------------------------------------------------   
  102. Predicate Information (identified by operation id):  
  103. ---------------------------------------------------   
  104.    2 - filter("N"<=990)  
  105.   
  106. --返回小部分数据时,使用的是索引扫描   
  107. scott@ORA11G> select count(pad) from t where n<=10;  
  108.   
  109. Execution Plan  
  110. ----------------------------------------------------------   
  111. Plan hash value: 4270555908  
  112. -------------------------------------------------------------------------------------   
  113. | Id  | Operation                    | Name | Rows  | Bytes | Cost (%CPU)| Time     |  
  114. -------------------------------------------------------------------------------------   
  115. |   0 | SELECT STATEMENT             |      |     1 |   105 |     3   (0)| 00:00:01 |  
  116. |   1 |  SORT AGGREGATE              |      |     1 |   105 |            |          |  
  117. |   2 |   TABLE ACCESS BY INDEX ROWID| T    |    10 |  1050 |     3   (0)| 00:00:01 |  
  118. |*  3 |    INDEX RANGE SCAN          | T_PK |    10 |       |     2   (0)| 00:00:01 |  
  119. -------------------------------------------------------------------------------------   
  120. Predicate Information (identified by operation id):  
  121. ---------------------------------------------------   
  122.    3 - access("N"<=10)  
  123.   
  124.   
  125. d、使用并行方式访问表时使用了全表扫描  
  126. scott@ORA11G> select /*+ parallel(3) */ count(pad) from t where n<=10;  
  127.   
  128. Execution Plan  
  129. ----------------------------------------------------------   
  130. Plan hash value: 3126468333  
  131. ----------------------------------------------------------------------------------------------------------------   
  132. | Id  | Operation              | Name     | Rows  | Bytes | Cost (%CPU)| Time     |    TQ  |IN-OUT| PQ Distrib |  
  133. ----------------------------------------------------------------------------------------------------------------   
  134. |   0 | SELECT STATEMENT       |          |     1 |   105 |     3   (0)| 00:00:01 |        |      |            |  
  135. |   1 |  SORT AGGREGATE        |          |     1 |   105 |            |          |        |      |            |  
  136. |   2 |   PX COORDINATOR       |          |       |       |            |          |        |      |            |  
  137. |   3 |    PX SEND QC (RANDOM) | :TQ10000 |     1 |   105 |            |          |  Q1,00 | P->S | QC (RAND)  |  
  138. |   4 |     SORT AGGREGATE     |          |     1 |   105 |            |          |  Q1,00 | PCWP |            |  
  139. |   5 |      PX BLOCK ITERATOR |          |    10 |  1050 |     3   (0)| 00:00:01 |  Q1,00 | PCWC |            |  
  140. |*  6 |       TABLE ACCESS FULL| T        |    10 |  1050 |     3   (0)| 00:00:01 |  Q1,00 | PCWP |            |  
  141. ----------------------------------------------------------------------------------------------------------------   
  142. Predicate Information (identified by operation id):  
  143. ---------------------------------------------------   
  144.    6 - filter("N"<=10)  
  145. Note  
  146. -----   
  147.    - Degree of Parallelism is 3 because of hint  
  148. --Author : Robinson   
  149. --Blog   : http://blog.csdn.net/robinson_0612   
  150.   
  151.   
  152. e、使用full提示时使用了全表扫描  
  153. scott@ORA11G> select /*+ full(t) */ count(pad) from t where n<=10;  
  154.   
  155. Execution Plan  
  156. ----------------------------------------------------------   
  157. Plan hash value: 2966233522  
  158. ---------------------------------------------------------------------------   
  159. | Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |  
  160. ---------------------------------------------------------------------------   
  161. |   0 | SELECT STATEMENT   |      |     1 |   105 |     7   (0)| 00:00:01 |  
  162. |   1 |  SORT AGGREGATE    |      |     1 |   105 |            |          |  
  163. |*  2 |   TABLE ACCESS FULL| T    |    10 |  1050 |     7   (0)| 00:00:01 |  
  164. ---------------------------------------------------------------------------   
  165. Predicate Information (identified by operation id):  
  166. ---------------------------------------------------   
  167.    2 - filter("N"<=10)           
  168.   
  169.   
  170. f、统计信息缺失导致全表扫描的情形  
  171. scott@ORA11G> exec dbms_stats.delete_table_stats('SCOTT','T');  
  172.   
  173. PL/SQL procedure successfully completed.  
  174.   
  175. scott@ORA11G> select count(pad) from t where n<=10;  
  176.   
  177. Execution Plan  
  178. ----------------------------------------------------------   
  179. Plan hash value: 2966233522  
  180. ---------------------------------------------------------------------------   
  181. | Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |  
  182. ---------------------------------------------------------------------------   
  183. |   0 | SELECT STATEMENT   |      |     1 |    65 |     7   (0)| 00:00:01 |  
  184. |   1 |  SORT AGGREGATE    |      |     1 |    65 |            |          |  
  185. |*  2 |   TABLE ACCESS FULL| T    |    10 |   650 |     7   (0)| 00:00:01 |  
  186. ---------------------------------------------------------------------------   
  187. Predicate Information (identified by operation id):  
  188. ---------------------------------------------------   
  189.    2 - filter("N"<=10)  
  190. Note  
  191. -----   
  192.    - dynamic sampling used for this statement (level=2)  
  193.   
  194. --上面的执行计划使用了全表扫描,而且提示使用了动态采样,也就是缺乏统计信息   
  195. --表上的数据块小于DB_FILE_MULTIBLOCK_READ_COUNT值的情形可能产生全表扫描的情形不演示  
a、准备演示环境
scott@ORA11G> select * from v$version where rownum<2;

BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production

--创建表t
scott@ORA11G> CREATE TABLE t 
  2  AS
  3  SELECT rownum AS n, rpad('*',100,'*') AS pad 
  4  FROM dual
  5  CONNECT BY level <= 1000;

Table created.

--添加索引
scott@ORA11G> create unique index t_pk on t(n);

Index created.

scott@ORA11G> alter table t add constraint t_pk primary key(n) using index t_pk;

Table altered.

--收集统计信息
scott@ORA11G> execute dbms_stats.gather_table_stats('SCOTT','T',cascade=>true);

PL/SQL procedure successfully completed.

scott@ORA11G> set autot trace exp;
scott@ORA11G> select count(*) from t;   --->count(*)的时候使用了索引快速扫描

Execution Plan
----------------------------------------------------------
Plan hash value: 454320086
----------------------------------------------------------------------
| Id  | Operation             | Name | Rows  | Cost (%CPU)| Time     |
----------------------------------------------------------------------
|   0 | SELECT STATEMENT      |      |     1 |     2   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE       |      |     1 |            |          |
|   2 |   INDEX FAST FULL SCAN| T_PK |  1000 |     2   (0)| 00:00:01 |
----------------------------------------------------------------------

scott@ORA11G> set autot off;
scott@ORA11G> alter table t move;  --->进行move table

Table altered.

-->move 之后索引失效,如下所示
scott@ORA11G> @idx_info           
Enter value for owner: scott
Enter value for table_name: t

Table Name    INDEX_NAME     CL_NAM               CL_POS STATUS   IDX_TYP         DSCD
------------- -------------- -------------------- ------ -------- --------------- ----
T             T_PK           N                         1 UNUSABLE NORMAL          ASC


b、索引失效导致全表扫描
scott@ORA11G> set autot trace exp;
scott@ORA11G> select count(*) from t;  

Execution Plan
----------------------------------------------------------
Plan hash value: 2966233522
-------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Cost (%CPU)| Time     |
-------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     1 |     7   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE    |      |     1 |            |          |
|   2 |   TABLE ACCESS FULL| T    |  1000 |     7   (0)| 00:00:01 |
-------------------------------------------------------------------

scott@ORA11G> set autot off;
scott@ORA11G> alter index t_pk rebuild;   -->重建索引

Index altered.

scott@ORA11G> @idx_info
Enter value for owner: scott
Enter value for table_name: t

Table Name     INDEX_NAME       CL_NAM               CL_POS STATUS   IDX_TYP         DSCD
-------------- ---------------- -------------------- ------ -------- --------------- ----
T              T_PK             N                         1 VALID    NORMAL          ASC


c、返回了整个表的大部分数据使用了全表扫描
scott@ORA11G> select count(pad) from t where n<=990;

Execution Plan
----------------------------------------------------------
Plan hash value: 2966233522
---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     1 |   105 |     7   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE    |      |     1 |   105 |            |          |
|*  2 |   TABLE ACCESS FULL| T    |   991 |   101K|     7   (0)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter("N"<=990)

--返回小部分数据时,使用的是索引扫描
scott@ORA11G> select count(pad) from t where n<=10;

Execution Plan
----------------------------------------------------------
Plan hash value: 4270555908
-------------------------------------------------------------------------------------
| Id  | Operation                    | Name | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |      |     1 |   105 |     3   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE              |      |     1 |   105 |            |          |
|   2 |   TABLE ACCESS BY INDEX ROWID| T    |    10 |  1050 |     3   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN          | T_PK |    10 |       |     2   (0)| 00:00:01 |
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   3 - access("N"<=10)


d、使用并行方式访问表时使用了全表扫描
scott@ORA11G> select /*+ parallel(3) */ count(pad) from t where n<=10;

Execution Plan
----------------------------------------------------------
Plan hash value: 3126468333
----------------------------------------------------------------------------------------------------------------
| Id  | Operation              | Name     | Rows  | Bytes | Cost (%CPU)| Time     |    TQ  |IN-OUT| PQ Distrib |
----------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |          |     1 |   105 |     3   (0)| 00:00:01 |        |      |            |
|   1 |  SORT AGGREGATE        |          |     1 |   105 |            |          |        |      |            |
|   2 |   PX COORDINATOR       |          |       |       |            |          |        |      |            |
|   3 |    PX SEND QC (RANDOM) | :TQ10000 |     1 |   105 |            |          |  Q1,00 | P->S | QC (RAND)  |
|   4 |     SORT AGGREGATE     |          |     1 |   105 |            |          |  Q1,00 | PCWP |            |
|   5 |      PX BLOCK ITERATOR |          |    10 |  1050 |     3   (0)| 00:00:01 |  Q1,00 | PCWC |            |
|*  6 |       TABLE ACCESS FULL| T        |    10 |  1050 |     3   (0)| 00:00:01 |  Q1,00 | PCWP |            |
----------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   6 - filter("N"<=10)
Note
-----
   - Degree of Parallelism is 3 because of hint
--Author : Robinson
--Blog   : http://blog.csdn.net/robinson_0612


e、使用full提示时使用了全表扫描
scott@ORA11G> select /*+ full(t) */ count(pad) from t where n<=10;

Execution Plan
----------------------------------------------------------
Plan hash value: 2966233522
---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     1 |   105 |     7   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE    |      |     1 |   105 |            |          |
|*  2 |   TABLE ACCESS FULL| T    |    10 |  1050 |     7   (0)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter("N"<=10)         


f、统计信息缺失导致全表扫描的情形
scott@ORA11G> exec dbms_stats.delete_table_stats('SCOTT','T');

PL/SQL procedure successfully completed.

scott@ORA11G> select count(pad) from t where n<=10;

Execution Plan
----------------------------------------------------------
Plan hash value: 2966233522
---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     1 |    65 |     7   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE    |      |     1 |    65 |            |          |
|*  2 |   TABLE ACCESS FULL| T    |    10 |   650 |     7   (0)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter("N"<=10)
Note
-----
   - dynamic sampling used for this statement (level=2)

--上面的执行计划使用了全表扫描,而且提示使用了动态采样,也就是缺乏统计信息
--表上的数据块小于DB_FILE_MULTIBLOCK_READ_COUNT值的情形可能产生全表扫描的情形不演示

4、全表扫描何时低效?

[sql] view plain copy print ?
  1. --先来做几个实验   
  2. a、演示表上的相关信息  
  3. scott@ORA11G> @idx_info  
  4. Enter value for owner: scott  
  5. Enter value for table_name: big_table  
  6.   
  7. Table Name                Index Name                CL_NAM    CL_POS Status   IDX_TYP         DSCD  
  8. ------------------------- ------------------------- --------- ------ -------- --------------- ----   
  9. BIG_TABLE                 BIG_TABLE_PK              ID             1 VALID    NORMAL          ASC  
  10.   
  11. scott@ORA11G> @idx_stat  
  12. Enter value for input_table_name: big_table  
  13. Enter value for owner: scott  
  14.   
  15.                                                      AVG LEAF BLKS AVG DATA BLKS  
  16. BLEV IDX_NAME        LEAF_BLKS   DST_KEYS       PER KEY       PER KEY CLUST_FACT LAST_ANALYZED         TB_BLKS    TB_ROWS  
  17. ---- -------------- ---------- ---------- ------------- ------------- ---------- ------------------ ---------- ----------   
  18.    1 BIG_TABLE_PK          208     100000             1             1       1483 20130524 10:45:51        1515     100000  
  19.   
  20. --数据库参数设置   
  21. scott@ORA11G> show parameter optimizer_index_  
  22.   
  23. NAME                                 TYPE        VALUE  
  24. ------------------------------------ ----------- ------------------------------   
  25. optimizer_index_caching              integer     0  
  26. optimizer_index_cost_adj             integer     100  
  27. scott@ORA11G> show parameter optimizer_mode  
  28.   
  29. NAME                                 TYPE        VALUE  
  30. ------------------------------------ ----------- ------------------------------   
  31. optimizer_mode                       string      ALL_ROWS  
  32.   
  33.   
  34. b、查询返回20%数据行的情形  
  35. scott@ORA11G> alter system flush buffer_cache;                                                    
  36. scott@ORA11G> select sum(object_id),avg(object_id) from big_table where id between 20000 and 40000;  
  37.   
  38. Execution Plan  
  39. ----------------------------------------------------------   
  40. Plan hash value: 3098837282                             -- 执行计划中,使用了索引范围扫描   
  41. ---------------------------------------------------------------------------------------------   
  42. | Id  | Operation                    | Name         | Rows  | Bytes | Cost (%CPU)| Time     |  
  43. ---------------------------------------------------------------------------------------------   
  44. |   0 | SELECT STATEMENT             |              |     1 |    18 |   341   (0)| 00:00:05 |  
  45. |   1 |  SORT AGGREGATE              |              |     1 |    18 |            |          |  
  46. |   2 |   TABLE ACCESS BY INDEX ROWID| BIG_TABLE    | 20046 |   352K|   341   (0)| 00:00:05 |  
  47. |*  3 |    INDEX RANGE SCAN          | BIG_TABLE_PK | 20046 |       |    43   (0)| 00:00:01 |  
  48. ---------------------------------------------------------------------------------------------   
  49. Predicate Information (identified by operation id):  
  50. ---------------------------------------------------   
  51.    3 - access("ID">=20000 AND "ID"<=40000)  
  52. Statistics  
  53. ----------------------------------------------------------   
  54.           0  recursive calls  
  55.           0  db block gets  
  56.         351  consistent gets  
  57.         351  physical reads  
  58.           0  redo size  
  59.         427  bytes sent via SQL*Net to client  
  60.         349  bytes received via SQL*Net from client  
  61.           2  SQL*Net roundtrips to/from client  
  62.           0  sorts (memory)  
  63.           0  sorts (disk)  
  64.           1  rows processed  
  65.   
  66. scott@ORA11G> alter system flush buffer_cache;   
  67. scott@ORA11G> select /*+ full(big_table) */ sum(object_id),avg(object_id) from big_table where id between 20000 and 40000;  
  68.   
  69. Execution Plan  
  70. ----------------------------------------------------------   
  71. Plan hash value: 599409829                ---- 使用了提示执行为全表扫描   
  72. --------------------------------------------------------------------------------   
  73. | Id  | Operation          | Name      | Rows  | Bytes | Cost (%CPU)| Time     |  
  74. --------------------------------------------------------------------------------   
  75. |   0 | SELECT STATEMENT   |           |     1 |    18 |   413   (1)| 00:00:05 |  
  76. |   1 |  SORT AGGREGATE    |           |     1 |    18 |            |          |  
  77. |*  2 |   TABLE ACCESS FULL| BIG_TABLE | 20046 |   352K|   413   (1)| 00:00:05 |  
  78. --------------------------------------------------------------------------------   
  79. Predicate Information (identified by operation id):  
  80. ---------------------------------------------------   
  81.    2 - filter("ID"<=40000 AND "ID">=20000)  
  82. Statistics  
  83. ----------------------------------------------------------   
  84.           0  recursive calls  
  85.           0  db block gets  
  86.        1486  consistent gets  
  87.        1484  physical reads  
  88.           0  redo size  
  89.         427  bytes sent via SQL*Net to client  
  90.         349  bytes received via SQL*Net from client  
  91.           2  SQL*Net roundtrips to/from client  
  92.           0  sorts (memory)  
  93.           0  sorts (disk)  
  94.           1  rows processed  
  95.   
  96. --注意对比上面两次操作中的consistent gets与physical reads   
  97.   
  98.   
  99. c、查询返回30%数据行的情形  
  100. scott@ORA11G> alter system flush buffer_cache;  
  101. scott@ORA11G> select sum(object_id),avg(object_id) from big_table where id between 20000 and 50000;  
  102.   
  103. Execution Plan  
  104. ----------------------------------------------------------   
  105. Plan hash value: 599409829             --->尽管返回数据的总行数为30%,而此时优化器使用了全表扫描   
  106. --------------------------------------------------------------------------------   
  107. | Id  | Operation          | Name      | Rows  | Bytes | Cost (%CPU)| Time     |  
  108. --------------------------------------------------------------------------------   
  109. |   0 | SELECT STATEMENT   |           |     1 |    18 |   413   (1)| 00:00:05 |  
  110. |   1 |  SORT AGGREGATE    |           |     1 |    18 |            |          |  
  111. |*  2 |   TABLE ACCESS FULL| BIG_TABLE | 30012 |   527K|   413   (1)| 00:00:05 |  
  112. --------------------------------------------------------------------------------   
  113. Predicate Information (identified by operation id):  
  114. ---------------------------------------------------   
  115.    2 - filter("ID"<=50000 AND "ID">=20000)  
  116. Statistics  
  117. ----------------------------------------------------------   
  118.           0  recursive calls  
  119.           0  db block gets  
  120.        1486  consistent gets  
  121.        1484  physical reads  
  122.           0  redo size  
  123.         427  bytes sent via SQL*Net to client  
  124.         349  bytes received via SQL*Net from client  
  125.           2  SQL*Net roundtrips to/from client  
  126.           0  sorts (memory)  
  127.           0  sorts (disk)  
  128.           1  rows processed  
  129.   
  130. --下面使用提示来强制优化器走索引扫描   
  131. scott@ORA11G> alter system flush buffer_cache;  
  132. scott@ORA11G> select /*+ index(big_table big_table_pk) */ sum(object_id),avg(object_id)   
  133.   2  from big_table where id between 20000 and 50000;  
  134.   
  135. Execution Plan  
  136. ----------------------------------------------------------   
  137. Plan hash value: 3098837282  
  138. ---------------------------------------------------------------------------------------------   
  139. | Id  | Operation                    | Name         | Rows  | Bytes | Cost (%CPU)| Time     |  
  140. ---------------------------------------------------------------------------------------------   
  141. |   0 | SELECT STATEMENT             |              |     1 |    18 |   511   (1)| 00:00:07 |  
  142. |   1 |  SORT AGGREGATE              |              |     1 |    18 |            |          |  
  143. |   2 |   TABLE ACCESS BY INDEX ROWID| BIG_TABLE    | 30012 |   527K|   511   (1)| 00:00:07 |  
  144. |*  3 |    INDEX RANGE SCAN          | BIG_TABLE_PK | 30012 |       |    64   (0)| 00:00:01 |  
  145. ---------------------------------------------------------------------------------------------   
  146. Predicate Information (identified by operation id):  
  147. ---------------------------------------------------   
  148.    3 - access("ID">=20000 AND "ID"<=50000)  
  149. Statistics  
  150. ----------------------------------------------------------   
  151.           0  recursive calls  
  152.           0  db block gets  
  153.         526  consistent gets  
  154.         526  physical reads  
  155.           0  redo size  
  156.         427  bytes sent via SQL*Net to client  
  157.         349  bytes received via SQL*Net from client  
  158.           2  SQL*Net roundtrips to/from client  
  159.           0  sorts (memory)  
  160.           0  sorts (disk)  
  161.           1  rows processed  
  162.             
  163. --注意观察每一次测试时所耗用的物理读与逻辑读   
  164. --从上面的测试可以看出,当表上所返回的数据行数接近于表上的30%时,Oracle 倾向于使用全表扫描   
  165. --而对于表上所返回的数据行数接近于表上的30%的情形,我们给与索引提示,此时比全表扫描更高效,即全表扫描是低效的   
  166. --笔者同时测试了数据返回总行数接近80%的情形以及创建了一个百万记录的进行对比测试   
  167. --大致结论,如果查询所返回的数据的总行数仅仅是表上数据的百分之八十以下,而使用了全表扫描,即可认为该全表扫描是低效的   
  168. --注:   
  169. --具体情况需要具体分析,如果你的表是千万级的,返回总数据的百分之零点几都会导致很大的差异   
  170. --其次,表上的索引应具有良好的聚簇因子,如不然,测试的结果可能有天壤之别   
  171. --最后,上面所描述的返回总行数应与执行结果返回的行数有差异,是指多少行参与了sum(object_id)  
--先来做几个实验
a、演示表上的相关信息
scott@ORA11G> @idx_info
Enter value for owner: scott
Enter value for table_name: big_table

Table Name                Index Name                CL_NAM    CL_POS Status   IDX_TYP         DSCD
------------------------- ------------------------- --------- ------ -------- --------------- ----
BIG_TABLE                 BIG_TABLE_PK              ID             1 VALID    NORMAL          ASC

scott@ORA11G> @idx_stat
Enter value for input_table_name: big_table
Enter value for owner: scott

                                                     AVG LEAF BLKS AVG DATA BLKS
BLEV IDX_NAME        LEAF_BLKS   DST_KEYS       PER KEY       PER KEY CLUST_FACT LAST_ANALYZED         TB_BLKS    TB_ROWS
---- -------------- ---------- ---------- ------------- ------------- ---------- ------------------ ---------- ----------
   1 BIG_TABLE_PK          208     100000             1             1       1483 20130524 10:45:51        1515     100000

--数据库参数设置
scott@ORA11G> show parameter optimizer_index_

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
optimizer_index_caching              integer     0
optimizer_index_cost_adj             integer     100
scott@ORA11G> show parameter optimizer_mode

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
optimizer_mode                       string      ALL_ROWS


b、查询返回20%数据行的情形
scott@ORA11G> alter system flush buffer_cache;                                                  
scott@ORA11G> select sum(object_id),avg(object_id) from big_table where id between 20000 and 40000;

Execution Plan
----------------------------------------------------------
Plan hash value: 3098837282                             -- 执行计划中,使用了索引范围扫描
---------------------------------------------------------------------------------------------
| Id  | Operation                    | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |              |     1 |    18 |   341   (0)| 00:00:05 |
|   1 |  SORT AGGREGATE              |              |     1 |    18 |            |          |
|   2 |   TABLE ACCESS BY INDEX ROWID| BIG_TABLE    | 20046 |   352K|   341   (0)| 00:00:05 |
|*  3 |    INDEX RANGE SCAN          | BIG_TABLE_PK | 20046 |       |    43   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   3 - access("ID">=20000 AND "ID"<=40000)
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
        351  consistent gets
        351  physical reads
          0  redo size
        427  bytes sent via SQL*Net to client
        349  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

scott@ORA11G> alter system flush buffer_cache; 
scott@ORA11G> select /*+ full(big_table) */ sum(object_id),avg(object_id) from big_table where id between 20000 and 40000;

Execution Plan
----------------------------------------------------------
Plan hash value: 599409829                ---- 使用了提示执行为全表扫描
--------------------------------------------------------------------------------
| Id  | Operation          | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |           |     1 |    18 |   413   (1)| 00:00:05 |
|   1 |  SORT AGGREGATE    |           |     1 |    18 |            |          |
|*  2 |   TABLE ACCESS FULL| BIG_TABLE | 20046 |   352K|   413   (1)| 00:00:05 |
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter("ID"<=40000 AND "ID">=20000)
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
       1486  consistent gets
       1484  physical reads
          0  redo size
        427  bytes sent via SQL*Net to client
        349  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

--注意对比上面两次操作中的consistent gets与physical reads


c、查询返回30%数据行的情形
scott@ORA11G> alter system flush buffer_cache;
scott@ORA11G> select sum(object_id),avg(object_id) from big_table where id between 20000 and 50000;

Execution Plan
----------------------------------------------------------
Plan hash value: 599409829             --->尽管返回数据的总行数为30%,而此时优化器使用了全表扫描
--------------------------------------------------------------------------------
| Id  | Operation          | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |           |     1 |    18 |   413   (1)| 00:00:05 |
|   1 |  SORT AGGREGATE    |           |     1 |    18 |            |          |
|*  2 |   TABLE ACCESS FULL| BIG_TABLE | 30012 |   527K|   413   (1)| 00:00:05 |
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter("ID"<=50000 AND "ID">=20000)
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
       1486  consistent gets
       1484  physical reads
          0  redo size
        427  bytes sent via SQL*Net to client
        349  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

--下面使用提示来强制优化器走索引扫描
scott@ORA11G> alter system flush buffer_cache;
scott@ORA11G> select /*+ index(big_table big_table_pk) */ sum(object_id),avg(object_id) 
  2  from big_table where id between 20000 and 50000;

Execution Plan
----------------------------------------------------------
Plan hash value: 3098837282
---------------------------------------------------------------------------------------------
| Id  | Operation                    | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |              |     1 |    18 |   511   (1)| 00:00:07 |
|   1 |  SORT AGGREGATE              |              |     1 |    18 |            |          |
|   2 |   TABLE ACCESS BY INDEX ROWID| BIG_TABLE    | 30012 |   527K|   511   (1)| 00:00:07 |
|*  3 |    INDEX RANGE SCAN          | BIG_TABLE_PK | 30012 |       |    64   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   3 - access("ID">=20000 AND "ID"<=50000)
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
        526  consistent gets
        526  physical reads
          0  redo size
        427  bytes sent via SQL*Net to client
        349  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed
          
--注意观察每一次测试时所耗用的物理读与逻辑读
--从上面的测试可以看出,当表上所返回的数据行数接近于表上的30%时,Oracle 倾向于使用全表扫描
--而对于表上所返回的数据行数接近于表上的30%的情形,我们给与索引提示,此时比全表扫描更高效,即全表扫描是低效的
--笔者同时测试了数据返回总行数接近80%的情形以及创建了一个百万记录的进行对比测试
--大致结论,如果查询所返回的数据的总行数仅仅是表上数据的百分之八十以下,而使用了全表扫描,即可认为该全表扫描是低效的
--注:
--具体情况需要具体分析,如果你的表是千万级的,返回总数据的百分之零点几都会导致很大的差异
--其次,表上的索引应具有良好的聚簇因子,如不然,测试的结果可能有天壤之别
--最后,上面所描述的返回总行数应与执行结果返回的行数有差异,是指多少行参与了sum(object_id)

5、小表的全表扫描是否高效?

[sql] view plain copy print ?
  1. --使用scott下dept表,仅有4行数据   
  2. scott@ORA11G> select * from dept where deptno>10;  
  3.   
  4. rows selected.  
  5.   
  6. Execution Plan  
  7. ----------------------------------------------------------   
  8. Plan hash value: 2985873453            --->执行计划选择了索引扫描   
  9. ---------------------------------------------------------------------------------------   
  10. | Id  | Operation                   | Name    | Rows  | Bytes | Cost (%CPU)| Time     |  
  11. ---------------------------------------------------------------------------------------   
  12. |   0 | SELECT STATEMENT            |         |     3 |    60 |     2   (0)| 00:00:01 |  
  13. |   1 |  TABLE ACCESS BY INDEX ROWID| DEPT    |     3 |    60 |     2   (0)| 00:00:01 |  
  14. |*  2 |   INDEX RANGE SCAN          | PK_DEPT |     3 |       |     1   (0)| 00:00:01 |  
  15. ---------------------------------------------------------------------------------------   
  16. Predicate Information (identified by operation id):  
  17. ---------------------------------------------------   
  18.    2 - access("DEPTNO">10)  
  19. Statistics  
  20. ----------------------------------------------------------   
  21.           0  recursive calls  
  22.           0  db block gets  
  23.           4  consistent gets                      -->使用了4次逻辑读   
  24.           0  physical reads  
  25.           0  redo size  
  26.         515  bytes sent via SQL*Net to client  
  27.         349  bytes received via SQL*Net from client  
  28.           2  SQL*Net roundtrips to/from client  
  29.           0  sorts (memory)  
  30.           0  sorts (disk)  
  31.           3  rows processed  
  32.   
  33. -->下面强制使用全表扫描   
  34. scott@ORA11G> select /*+ full(dept) */ * from dept where deptno>10;  
  35.   
  36. rows selected.  
  37.   
  38. Execution Plan  
  39. ----------------------------------------------------------   
  40. Plan hash value: 3383998547   
  41. --------------------------------------------------------------------------   
  42. | Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |  
  43. --------------------------------------------------------------------------   
  44. |   0 | SELECT STATEMENT  |      |     3 |    60 |     3   (0)| 00:00:01 |  
  45. |*  1 |  TABLE ACCESS FULL| DEPT |     3 |    60 |     3   (0)| 00:00:01 |  
  46. --------------------------------------------------------------------------   
  47. Predicate Information (identified by operation id):  
  48. ---------------------------------------------------   
  49.    1 - filter("DEPTNO">10)  
  50. Statistics  
  51. ----------------------------------------------------------   
  52.           1  recursive calls  
  53.           0  db block gets  
  54.           4  consistent gets         -->此时的逻辑读同样为4次   
  55.           0  physical reads  
  56.           0  redo size  
  57.         515  bytes sent via SQL*Net to client  
  58.         349  bytes received via SQL*Net from client  
  59.           2  SQL*Net roundtrips to/from client  
  60.           0  sorts (memory)  
  61.           0  sorts (disk)  
  62.           3  rows processed  
  63.   
  64. --下面来看看count(*)的情形   
  65. scott@ORA11G> select count(*) from dept;  
  66.   
  67. 1 row selected.  
  68.   
  69. Execution Plan  
  70. ----------------------------------------------------------   
  71. Plan hash value: 3051237957               --->执行计划选择了索引全扫描   
  72. --------------------------------------------------------------------   
  73. | Id  | Operation        | Name    | Rows  | Cost (%CPU)| Time     |  
  74. --------------------------------------------------------------------   
  75. |   0 | SELECT STATEMENT |         |     1 |     1   (0)| 00:00:01 |  
  76. |   1 |  SORT AGGREGATE  |         |     1 |            |          |  
  77. |   2 |   INDEX FULL SCAN| PK_DEPT |     4 |     1   (0)| 00:00:01 |  
  78. --------------------------------------------------------------------   
  79. Statistics  
  80. ----------------------------------------------------------   
  81.           0  recursive calls  
  82.           0  db block gets  
  83.           1  consistent gets            -->逻辑读仅为1次   
  84.           0  physical reads  
  85.           0  redo size  
  86.         335  bytes sent via SQL*Net to client  
  87.         349  bytes received via SQL*Net from client  
  88.           2  SQL*Net roundtrips to/from client  
  89.           0  sorts (memory)  
  90.           0  sorts (disk)  
  91.           1  rows processed  
  92.   
  93. -->下面强制使用全表扫描   
  94. scott@ORA11G> select /*+ full(dept) */ count(*) from dept;  
  95.   
  96. 1 row selected.  
  97.   
  98. Execution Plan  
  99. ----------------------------------------------------------   
  100. Plan hash value: 315352865  
  101. -------------------------------------------------------------------   
  102. | Id  | Operation          | Name | Rows  | Cost (%CPU)| Time     |  
  103. -------------------------------------------------------------------   
  104. |   0 | SELECT STATEMENT   |      |     1 |     3   (0)| 00:00:01 |  
  105. |   1 |  SORT AGGREGATE    |      |     1 |            |          |  
  106. |   2 |   TABLE ACCESS FULL| DEPT |     4 |     3   (0)| 00:00:01 |  
  107. -------------------------------------------------------------------   
  108. Statistics  
  109. ----------------------------------------------------------   
  110.           0  recursive calls  
  111.           0  db block gets  
  112.           3  consistent gets     -->使用了3次逻辑读   
  113.           0  physical reads  
  114.           0  redo size  
  115.         335  bytes sent via SQL*Net to client  
  116.         349  bytes received via SQL*Net from client  
  117.           2  SQL*Net roundtrips to/from client  
  118.           0  sorts (memory)  
  119.           0  sorts (disk)  
  120.           1  rows processed  
  121.                       
  122. --对于小表,从上面的情形可以看出,使用索引扫描也是比全表扫描高效   
  123. --因此,建议始终为小表建立索引  
--使用scott下dept表,仅有4行数据
scott@ORA11G> select * from dept where deptno>10;

3 rows selected.

Execution Plan
----------------------------------------------------------
Plan hash value: 2985873453            --->执行计划选择了索引扫描
---------------------------------------------------------------------------------------
| Id  | Operation                   | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |         |     3 |    60 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| DEPT    |     3 |    60 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | PK_DEPT |     3 |       |     1   (0)| 00:00:01 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("DEPTNO">10)
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          4  consistent gets                      -->使用了4次逻辑读
          0  physical reads
          0  redo size
        515  bytes sent via SQL*Net to client
        349  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          3  rows processed

-->下面强制使用全表扫描
scott@ORA11G> select /*+ full(dept) */ * from dept where deptno>10;

3 rows selected.

Execution Plan
----------------------------------------------------------
Plan hash value: 3383998547 
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     3 |    60 |     3   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| DEPT |     3 |    60 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("DEPTNO">10)
Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
          4  consistent gets         -->此时的逻辑读同样为4次
          0  physical reads
          0  redo size
        515  bytes sent via SQL*Net to client
        349  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          3  rows processed

--下面来看看count(*)的情形
scott@ORA11G> select count(*) from dept;

1 row selected.

Execution Plan
----------------------------------------------------------
Plan hash value: 3051237957               --->执行计划选择了索引全扫描
--------------------------------------------------------------------
| Id  | Operation        | Name    | Rows  | Cost (%CPU)| Time     |
--------------------------------------------------------------------
|   0 | SELECT STATEMENT |         |     1 |     1   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE  |         |     1 |            |          |
|   2 |   INDEX FULL SCAN| PK_DEPT |     4 |     1   (0)| 00:00:01 |
--------------------------------------------------------------------
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          1  consistent gets            -->逻辑读仅为1次
          0  physical reads
          0  redo size
        335  bytes sent via SQL*Net to client
        349  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

-->下面强制使用全表扫描
scott@ORA11G> select /*+ full(dept) */ count(*) from dept;

1 row selected.

Execution Plan
----------------------------------------------------------
Plan hash value: 315352865
-------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Cost (%CPU)| Time     |
-------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     1 |     3   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE    |      |     1 |            |          |
|   2 |   TABLE ACCESS FULL| DEPT |     4 |     3   (0)| 00:00:01 |
-------------------------------------------------------------------
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          3  consistent gets     -->使用了3次逻辑读
          0  physical reads
          0  redo size
        335  bytes sent via SQL*Net to client
        349  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed
                    
--对于小表,从上面的情形可以看出,使用索引扫描也是比全表扫描高效
--因此,建议始终为小表建立索引

更多参考

DML Error Logging 特性 

PL/SQL --> 游标

PL/SQL --> 隐式游标(SQL%FOUND)

批量SQL之 FORALL 语句

批量SQL之 BULK COLLECT 子句

PL/SQL 集合的初始化与赋值

PL/SQL 联合数组与嵌套表
PL/SQL 变长数组
PL/SQL --> PL/SQL记录

SQL tuning 步骤

高效SQL语句必杀技

父游标、子游标及共享游标

绑定变量及其优缺点

dbms_xplan之display_cursor函数的使用

dbms_xplan之display函数的使用

执行计划中各字段各模块描述

使用 EXPLAIN PLAN 获取SQL语句执行计划

上一篇: Oracle datapump expdp/impdp 导入导出数据库时hang住 下一篇: Oracle db_file_mulitblock_read_count参数

你可能感兴趣的:(oracle系统性能优化及管理,oracle转载博文未看)