列值的选择性、集簇和柱状图
切记,优化器了解表中列数据的许多重要特征,最显著的是列值的选择性和列的集簇因子。
例如,以下我们看到一个使用列值来形成结果集的查询:
select
customer_name
from
customer
where
customer_state
= 'Rhode
Island';
在此示例中,选择使用索引还是全表扫描受到罗得岛客户比例的影响。如果罗得岛客户的比例非常小,并且数值集簇在数据块中,则对于此查询而言索引扫描可能是最快的执行计划。
许多
Oracle 开发人员对于当他们只检索很少量的行时优化器选择全表扫描而感到困惑,而没有意识到优化器考虑了表中列值的集簇。
Oracle 在
dba_indexes 视图中提供一个名为 clustering_factor
的列,通知优化器关于表的行与索引的同步情况。当集簇因子接近数据块的数量时,表的行与索引同步。
列值的选择性、db_block_size、avg_row_len
以及集合基数全都协同工作,帮助优化器决定是使用索引还是使用全表扫描。如果数据列具有高度的选择性和低的
clustering_factor,则索引扫描通常是最快的执行方法(参见图 2)。
如果多数 SQL 引用了具有高 clustering_factor、大
db_block_size 和小 avg_row_len 的列,则 DBA 有时会周期性地对表的行进行重排序或使用单表集
www.cuug.com
簇来维持行的顺序。这种方法将所有相邻的行放置在同一数据块中,消除了全表扫描,使查询速度的增加高达
30 倍。
相反,高 clustering_factor 的数值达到表中的行数
(num_rows),表明这些行的顺序与索引中的顺序不同,索引范围扫描将会需要额外的 I/O。由于 clustering_factor
达到表中的行数,这些行与索引不同步。
但是,即使列具有高度的选择性,高 clustering_factor 和小 avg_row_len
也会表示列值在表中随机分布,而获取这些行需要额外的 I/O。在此情况下,索引范围扫描会导致大量不必要的
I/O(参见图3);全表扫描则会高效得多。
总而言之,clustering_factor、db_block_size 和 avg_row_len
全都影响优化器有关执行全表扫描或是索引范围扫描的决策,理解优化器如何使用这些统计信息非常重要。
我们已经注意到,每个新版本的优化器都有改进,并且
Oracle Database 10g 提供的最新增强特性在确定执行计划时会考虑外部的影响。Oracle 称此特性为外部成本核算,并包括对 CPU 和 I/O
成本的评估。
以下例子来说明列值的集簇因子对执行计划产生的影响:
1、搭建环境
SQL> insert into test select
* from emp;
14 rows created.
Commit complete.
SQL>create index
test_ename_ind on test (ename);
2、分析表
SQL> analyze table test compute
statistics;
Table analyzed.
3、查看列值的集簇因子:
select
t.table_name,t.NUM_ROWS,t.BLOCKS,t.AVG_ROW_LEN,i.index_name,i.CLUSTERING_FACTOR
www.cuug.com
from dba_tables t,dba_indexes
i
where t.table_name=i.table_name
and t.owner='SCOTT'
and
t.table_name='TEST3';
TABLE_NAME NUM_ROWS BLOCKS AVG_ROW_LEN INDEX_NAME
CLUSTERING_FACTOR
---------- ---------- ---------- ----------- ----------
-----------------
TEST 14 1 32 TEST_ENAME 1
4、使用索引列进行查询:
SQL> select
* from test where ename='CUUG1';
no rows selected
Execution
Plan
----------------------------------------------------------
0 SELECT
STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=32)
1 0 TABLE ACCESS (FULL)
OF 'TEST' (TABLE) (Cost=2 Card=1
Bytes
=32)
Statistics
----------------------------------------------------------
1
recursive calls
0 db block gets
3 consistent gets
0 physical reads
0
redo size
615 bytes sent via SQL*Net to client
376 bytes received via
SQL*Net from client
1 SQL*Net roundtrips to/from client
0 sorts
(memory)
0 sorts (disk)
0 rows
processed
注;这里执行计划采用全表扫描。(如果使用到了索引,那么再往表中插入一遍数据,然后做analyze,此时oracle就会选择全表扫描)
5、往test表中插入数据,注意ename列的值都不一样,也就是有很高的选择性。
declare
V_ENAME
test.ename%type;
www.cuug.com
BEGIN
V_ENAME :='CUUG';
FOR
I IN 1..10000 LOOP
insert into test (empno,ename,job,sal,deptno)
values
(1121,V_ENAME||I,'WORKER',2000,20);
END
LOOP;
COMMIT;
END;
6、然后使用索引列进行查询:
SQL> select * from test where
ename='CUUG1';
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
----------
---------- --------- ---------- --------- ---------- ----------
----------
1121 CUUG1 WORKER 2000 20
Execution
Plan
----------------------------------------------------------
0 SELECT
STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=32)
1 0 TABLE ACCESS (FULL)
OF 'TEST' (TABLE) (Cost=2 Card=1
Bytes
=32)
Statistics
----------------------------------------------------------
1
recursive calls
0 db block gets
51 consistent gets
0 physical
reads
0 redo size
795 bytes sent via SQL*Net to client
511 bytes
received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0
sorts (memory)
0 sorts (disk)
1 rows
processed
注:这里还是采用了全表扫描的执行计划,按道理列的选择性很高,AVG_ROW_LEN的值也很小,为什么没有采用索引能,一个关键的原因就是集簇因子。
www.cuug.com
因为test表曾经分析过,在dba_indexes中保留了列值的集簇因子,导致执行计划选择全表扫描。
7、如何让执行计划采用索引,需要对表test再次分析。
SQL>
analyze table test compute statistics;
Table analyzed.
1 select
t.table_name,t.NUM_ROWS,t.BLOCKS,t.AVG_ROW_LEN,i.index_name,i.CLUSTERING_FACTOR
2
from dba_tables t,dba_indexes i
3 where t.table_name=i.table_name
4 and
t.owner='SCOTT'
5* and t.table_name='TEST3'
SQL> /
TABLE_NAME
NUM_ROWS BLOCKS AVG_ROW_LEN INDEX_NAME
CLUSTERING_FACTOR
------------------------------ ---------- ----------
----------- ------------------------------ -----------------
TEST 10014 47 32
TEST_ENAME 1920
注:这里的集簇因子的值变了。
8、再次执行查询:
SQL> select * from test
where ename='CUUG1';
EMPNO ENAME JOB MGR HIREDATE SAL COMM
DEPTNO
---------- ---------- --------- ---------- --------- ----------
---------- ----------
1121 CUUG1 WORKER 2000 20
Execution
Plan
----------------------------------------------------------
0 SELECT
STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=29)
1 0 TABLE ACCESS (BY
INDEX ROWID) OF 'TEST' (TABLE) (Cost=2 Ca
rd=1 Bytes=29)
2 1 INDEX (RANGE
SCAN) OF 'TEST_ENAME' (INDEX) (Cost=1 Card=
1)
www.cuug.com
www.cuug.com
Statistics
----------------------------------------------------------
1
recursive calls
0 db block gets
4 consistent gets
0 physical reads
0
redo size
799 bytes sent via SQL*Net to client
511 bytes received via
SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts
(memory)
0 sorts (disk)
1 rows processed
注:发现这次执行计划选择了索引。
许多 Oracle
开发人员对于当他们只检索很少量的行时优化器选择全表扫描而感到困惑,而没有意识到优化器考虑了表中列值的集簇。
另外,如果表包含有很多空块(delete以后),也会影响优化器的选择,比如集簇因子不同步,但是空块很多,也会影响优化器。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/500314/viewspace-1063680/
,如需转载,请注明出处,否则将追究法律责任。