索引重建的数据源(二)

对这个问题有了进一步的认识。
索引重建的数据源:http://yangtingkun.itpub.net/post/468/457384
上一篇文章测试的结果认为DDL也是基于CBO的,但是今天发现问题并非如此。Oracle在评估REBUILD索引时并不是根据统计信息,而是根据数据字典中非索引字段的长度:

SQL> CREATE TABLE t_rebuild (id NUMBER, flag CHAR(1));
TABLE created.
SQL> INSERT INTO t_rebuild SELECT rownum, 'a' FROM dba_objects;
15695 ROWS created.
SQL> commit;
Commit complete.
SQL> CREATE INDEX ind_t_rebuild_id ON t_rebuild(id);
INDEX created.
SQL> EXPLAIN plan FOR ALTER INDEX ind_t_rebuild_id rebuild;
Explained.
SQL> SELECT * FROM TABLE(dbms_xplan.display);
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------
Plan hash VALUE: 3014377519
-------------------------------------------------------------------------------------------
| Id  | Operation              | Name             | ROWS  | Bytes | Cost (%CPU)| TIME     |
-------------------------------------------------------------------------------------------
|   0 | ALTER INDEX STATEMENT  |                  |    82 |  1066 |     2   (0)| 00:00:01 |
|   1 |  INDEX BUILD NON UNIQUE| IND_T_REBUILD_ID |       |       |            |          |
|   2 |   SORT CREATE INDEX    |                  |    82 |  1066 |            |          |
|   3 |    TABLE ACCESS FULL   | T_REBUILD        |    82 |  1066 |     2   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------
10 ROWS selected.
SQL> ALTER TABLE t_rebuild MODIFY (flag CHAR(2));
TABLE altered.
SQL> EXPLAIN plan FOR ALTER INDEX ind_t_rebuild_id rebuild;
Explained.
SQL> SELECT * FROM TABLE(dbms_xplan.display);
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------
Plan hash VALUE: 3014377519
-------------------------------------------------------------------------------------------
| Id  | Operation              | Name             | ROWS  | Bytes | Cost (%CPU)| TIME     |
-------------------------------------------------------------------------------------------
|   0 | ALTER INDEX STATEMENT  |                  |  2288 | 29744 |     7   (0)| 00:00:01 |
|   1 |  INDEX BUILD NON UNIQUE| IND_T_REBUILD_ID |       |       |            |          |
|   2 |   SORT CREATE INDEX    |                  |  2288 | 29744 |            |          |
|   3 |    TABLE ACCESS FULL   | T_REBUILD        |  2288 | 29744 |     7   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------
10 ROWS selected.
SQL> ALTER TABLE t_rebuild MODIFY (flag CHAR(3));
TABLE altered.
SQL> EXPLAIN plan FOR ALTER INDEX ind_t_rebuild_id rebuild;
Explained.
SQL> SELECT * FROM TABLE(dbms_xplan.display);
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------
Plan hash VALUE: 43729923
-------------------------------------------------------------------------------------------
| Id  | Operation              | Name             | ROWS  | Bytes | Cost (%CPU)| TIME     |
-------------------------------------------------------------------------------------------
|   0 | ALTER INDEX STATEMENT  |                  |  4738 | 61594 |    13   (0)| 00:00:01 |
|   1 |  INDEX BUILD NON UNIQUE| IND_T_REBUILD_ID |       |       |            |          |
|   2 |   SORT CREATE INDEX    |                  |  4738 | 61594 |            |          |
|   3 |    INDEX FAST FULL SCAN| IND_T_REBUILD_ID |       |       |            |          |
-------------------------------------------------------------------------------------------
10 ROWS selected.

随着非索引列的长度增加,重建索引的执行计划由全表扫描变成了索引快速全扫。
整个过程并没有收集过表或索引的统计信息,但是执行计划已经发生了改变,下面尝试关闭统计信息动态收集,以及设置表和列属性的方式影响执行计划:

SQL> ALTER SESSION SET optimizer_dynamic_sampling = 0;
SESSION altered.
SQL> EXPLAIN plan FOR ALTER INDEX ind_t_rebuild_id rebuild;
Explained.
SQL> SELECT * FROM TABLE(dbms_xplan.display);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------
Plan hash VALUE: 43729923
-------------------------------------------------------------------------------------------
| Id  | Operation              | Name             | ROWS  | Bytes | Cost (%CPU)| TIME     |
-------------------------------------------------------------------------------------------
|   0 | ALTER INDEX STATEMENT  |                  |  4738 | 61594 |    13   (0)| 00:00:01 |
|   1 |  INDEX BUILD NON UNIQUE| IND_T_REBUILD_ID |       |       |            |          |
|   2 |   SORT CREATE INDEX    |                  |  4738 | 61594 |            |          |
|   3 |    INDEX FAST FULL SCAN| IND_T_REBUILD_ID |       |       |            |          |
-------------------------------------------------------------------------------------------
10 ROWS selected.
SQL> EXEC dbms_stats.set_table_stats(USER, 'T_REBUILD', numrows => 1, numblks => 1, avgrlen => 2)
PL/SQL PROCEDURE successfully completed.
SQL> EXPLAIN plan FOR ALTER INDEX ind_t_rebuild_id rebuild;
Explained.
SQL> SELECT * FROM TABLE(dbms_xplan.display);
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------
Plan hash VALUE: 43729923
-------------------------------------------------------------------------------------------
| Id  | Operation              | Name             | ROWS  | Bytes | Cost (%CPU)| TIME     |
-------------------------------------------------------------------------------------------
|   0 | ALTER INDEX STATEMENT  |                  |     1 |     2 |     2   (0)| 00:00:01 |
|   1 |  INDEX BUILD NON UNIQUE| IND_T_REBUILD_ID |       |       |            |          |
|   2 |   SORT CREATE INDEX    |                  |     1 |     2 |            |          |
|   3 |    INDEX FAST FULL SCAN| IND_T_REBUILD_ID |       |       |            |          |
-------------------------------------------------------------------------------------------
10 ROWS selected.
SQL> EXEC dbms_stats.set_column_stats(USER, 'T_REBUILD', 'FLAG', avgclen => 1)
PL/SQL PROCEDURE successfully completed.
SQL> EXPLAIN plan FOR ALTER INDEX ind_t_rebuild_id rebuild;
Explained.
SQL> SELECT * FROM TABLE(dbms_xplan.display);
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------
Plan hash VALUE: 43729923
-------------------------------------------------------------------------------------------
| Id  | Operation              | Name             | ROWS  | Bytes | Cost (%CPU)| TIME     |
-------------------------------------------------------------------------------------------
|   0 | ALTER INDEX STATEMENT  |                  |     1 |     2 |     2   (0)| 00:00:01 |
|   1 |  INDEX BUILD NON UNIQUE| IND_T_REBUILD_ID |       |       |            |          |
|   2 |   SORT CREATE INDEX    |                  |     1 |     2 |            |          |
|   3 |    INDEX FAST FULL SCAN| IND_T_REBUILD_ID |       |       |            |          |
-------------------------------------------------------------------------------------------
10 ROWS selected.

很明显DDL执行计划的确定其实和统计信息没有什么关系,而完全是根据数据字典确定的。因此这实际上也是一种RULE,只不过Oracle将这个条件写到了优化器中,如果将优化器设置为RULE,Oracle同样可以做出相同的判断:

SQL> ALTER SESSION SET optimizer_mode = rule;
SESSION altered.
SQL> DROP TABLE t_rebuild purge;
TABLE dropped.
SQL> CREATE TABLE t_rebuild (id NUMBER, flag CHAR(1));
TABLE created.
SQL> INSERT INTO t_rebuild SELECT rownum, 'a' FROM dba_objects;
15695 ROWS created.
SQL> commit;
Commit complete.
SQL> CREATE INDEX ind_t_rebuild_id ON t_rebuild(id);
INDEX created.
SQL> EXPLAIN plan FOR ALTER INDEX ind_t_rebuild_id rebuild;
Explained.
SQL> SELECT * FROM TABLE(dbms_xplan.display);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------
Plan hash VALUE: 3014377519
---------------------------------------------------
| Id  | Operation              | Name             |
---------------------------------------------------
|   0 | ALTER INDEX STATEMENT  |                  |
|   1 |  INDEX BUILD NON UNIQUE| IND_T_REBUILD_ID |
|   2 |   SORT CREATE INDEX    |                  |
|   3 |    TABLE ACCESS FULL   | T_REBUILD        |
---------------------------------------------------
Note
-----
   - rule based optimizer used (consider USING cbo)
14 ROWS selected.
SQL> ALTER TABLE t_rebuild MODIFY flag CHAR(3);
TABLE altered.
SQL> EXPLAIN plan FOR ALTER INDEX ind_t_rebuild_id rebuild;
Explained.
SQL> SELECT * FROM TABLE(dbms_xplan.display);
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------
Plan hash VALUE: 43729923
 
---------------------------------------------------
| Id  | Operation              | Name             |
---------------------------------------------------
|   0 | ALTER INDEX STATEMENT  |                  |
|   1 |  INDEX BUILD NON UNIQUE| IND_T_REBUILD_ID |
|   2 |   SORT CREATE INDEX    |                  |
|   3 |    INDEX FAST FULL SCAN| IND_T_REBUILD_ID |
---------------------------------------------------
Note
-----
   - rule based optimizer used (consider USING cbo)
14 ROWS selected.

不过由于很多DDL操作对于的表或对象本身就没有统计信息,完全使用CBO是不现实的,也是不准确的,所以采用这种基于规则的执行计划也是有道理的。不过事实上,对于DDL而言,有多种执行计划可选择的其实也并不多。

This entry was posted in ORACLE and tagged , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *