Jac*_*las 5 oracle optimization oracle-11g-r2
在下面的脚本中,CBO 选择INDEX FAST FULL SCAN
了一个INDEX SKIP SCAN
(这通过使用 UNION ALL 的查询来说明,但您可以证明它是正确的,只需explain plan for select * from foo where baz=1
)。尽管跳过扫描是成本较低的选择,但还是做出了这一选择。
一旦表被分析,跳过扫描是首选(尽管相对成本实际上上升)。CBO 似乎根本不考虑使用动态采样进行跳过扫描,这是真的吗?如果没有,为什么在收集统计数据之前没有选择跳过扫描?
架构:
create table foo( bar integer,
baz integer,
qux char(99),
constraint pk_foo primary key (bar, baz) )
organization index compress;
--table FOO created.
insert into foo(bar,baz) select mod(level,1000), level from dual connect by level<1000000;
--999,999 rows inserted.
commit;
--committed.
Run Code Online (Sandbox Code Playgroud)
预分析:
explain plan for
select * from foo where baz=1
union all
select /*+ full(foo) */ * from foo where baz=1
union all
select /*+ index_ss(foo) */ * from foo where baz=1;
--plan FOR succeeded.
select * from table(dbms_xplan.display);
/*
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 132 | 16764 | 8480 (53)| 00:01:34 |
| 1 | UNION-ALL | | | | | |
|* 2 | INDEX FAST FULL SCAN| PK_FOO | 44 | 5588 | 4034 (1)| 00:00:45 | <-- why full?
|* 3 | INDEX FAST FULL SCAN| PK_FOO | 44 | 5588 | 4034 (1)| 00:00:45 |
|* 4 | INDEX SKIP SCAN | PK_FOO | 44 | 5588 | 411 (0)| 00:00:05 |
--------------------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement (level=2)
*/
Run Code Online (Sandbox Code Playgroud)
分析:
analyze table foo compute statistics;
--table FOO analyzed.
Run Code Online (Sandbox Code Playgroud)
事后分析:
explain plan for
select * from foo where baz=1
union all
select /*+ full(foo) */ * from foo where baz=1
union all
select /*+ index_ss(foo) */ * from foo where baz=1;
--plan FOR succeeded.
select * from table(dbms_xplan.display);
/*
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 3 | 30 | 6038 (84)| 00:01:07 |
| 1 | UNION-ALL | | | | | |
|* 2 | INDEX SKIP SCAN | PK_FOO | 1 | 10 | 1003 (1)| 00:00:12 | <-- makes sense
|* 3 | INDEX FAST FULL SCAN| PK_FOO | 1 | 10 | 4033 (1)| 00:00:45 |
|* 4 | INDEX SKIP SCAN | PK_FOO | 1 | 10 | 1003 (1)| 00:00:12 |
--------------------------------------------------------------------------------
*/
Run Code Online (Sandbox Code Playgroud)
CBO 似乎根本不考虑使用动态采样进行跳过扫描,这是真的吗?
其实这很容易验证,你可以通过启用 10053 跟踪来做到这一点。您会看到优化器甚至根本不考虑跳过扫描。原因是“_optimizer_skip_scan_guess”参数。此参数的默认值是 FALSE,这意味着优化器在它所拥有的只是“猜测”选择性时不会考虑跳过扫描,动态采样就是这种情况。
如果您将“_optimizer_skip_scan_guess”设置为 TRUE,则将考虑跳过扫描,这也可以再次通过 10053 跟踪确认。
PS:您的 db_file_multiblock_read_count 参数似乎低于默认值。在我的11.2.0.4沙箱上,默认值为128,在对表进行统计后,索引FFS的开销大约是索引SS的三分之一。
编辑:添加输出
SQL> create table foo( bar integer, baz integer, qux char(99), constraint pk_foo primary key (bar, baz) ) organization index compress;
Table created.
SQL> insert into foo(bar,baz) select mod(level,1000), level from dual connect by level<1000000;
999999 rows created.
SQL> commit;
Commit complete.
Run Code Online (Sandbox Code Playgroud)
原来的:
SQL> explain plan for
select * from foo where baz=1
union all
select /*+ full(foo) */ * from foo where baz=1
union all
select /*+ index_ss(foo) */ * from foo where baz=1; 2 3 4 5 6
Explained.
SQL> select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 1715140356
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 66 | 8382 | 1302 (1)| 00:00:16 |
| 1 | UNION-ALL | | | | | |
|* 2 | INDEX FAST FULL SCAN| PK_FOO | 22 | 2794 | 546 (1)| 00:00:07 |
|* 3 | INDEX FAST FULL SCAN| PK_FOO | 22 | 2794 | 546 (1)| 00:00:07 |
|* 4 | INDEX SKIP SCAN | PK_FOO | 22 | 2794 | 211 (0)| 00:00:03 |
--------------------------------------------------------------------------------
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("BAZ"=1)
3 - filter("BAZ"=1)
4 - access("BAZ"=1)
filter("BAZ"=1)
Note
-----
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
- dynamic sampling used for this statement (level=2)
Run Code Online (Sandbox Code Playgroud)
为猜测的选择性启用跳过扫描:
SQL> alter session set "_optimizer_skip_scan_guess"=true;
Session altered.
SQL> explain plan for
select * from foo where baz=1
union all
select /*+ full(foo) */ * from foo where baz=1
union all
select /*+ index_ss(foo) */ * from foo where baz=1; 2 3 4 5 6
Explained.
SQL> select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 3033162421
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 66 | 8382 | 772 (1)| 00:00:10 |
| 1 | UNION-ALL | | | | | |
|* 2 | INDEX SKIP SCAN | PK_FOO | 22 | 2794 | 211 (0)| 00:00:03 |
|* 3 | INDEX FAST FULL SCAN| PK_FOO | 22 | 2794 | 350 (1)| 00:00:05 |
|* 4 | INDEX SKIP SCAN | PK_FOO | 22 | 2794 | 211 (0)| 00:00:03 |
--------------------------------------------------------------------------------
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("BAZ"=1)
filter("BAZ"=1)
3 - filter("BAZ"=1)
4 - access("BAZ"=1)
filter("BAZ"=1)
Note
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
-----
- dynamic sampling used for this statement (level=2)
24 rows selected.
Run Code Online (Sandbox Code Playgroud)
只是为了完整性:
SQL> analyze table foo compute statistics;
Table analyzed.
SQL> explain plan for
select * from foo where baz=1
union all
select /*+ full(foo) */ * from foo where baz=1
union all
select /*+ index_ss(foo) */ * from foo where baz=1; 2 3 4 5 6
Explained.
SQL> select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 1715140356
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 3 | 30 | 1717 (2)| 00:00:21 |
| 1 | UNION-ALL | | | | | |
|* 2 | INDEX FAST FULL SCAN| PK_FOO | 1 | 10 | 357 (3)| 00:00:05 |
|* 3 | INDEX FAST FULL SCAN| PK_FOO | 1 | 10 | 357 (3)| 00:00:05 |
|* 4 | INDEX SKIP SCAN | PK_FOO | 1 | 10 | 1002 (0)| 00:00:13 |
--------------------------------------------------------------------------------
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("BAZ"=1)
3 - filter("BAZ"=1)
4 - access("BAZ"=1)
filter("BAZ"=1)
19 rows selected.
Run Code Online (Sandbox Code Playgroud)