您的位置:首页 > 数据库 > Oracle

ORACLE动态采样

2016-03-30 23:57 495 查看
原文地址:http://www.oracle.com/technetwork/issue-archive/2009/09-jan/o19asktom-086775.html

动态采样是为了解决当硬解析时对没有统计信息的表抽样收集统计信息,让优化器走出合适的执行计划。

从oracle9.2开始,动态采样共有11个级别(从0到10),oracle9.2的默认采样级别为1,oracle10.1及以上版本默认为2.

1.启用动态采样的方法:

  1.1 设置参数OPTIMIZER_DYNAMIC_SAMPLING

  1.2 会用hint DYNAMIC_SAMPLING

2.当优化器遇到没有统计信息的表时,会尝试动态的收集优化器需要的统计信息。但是对于remote tables和external tables,

无法对其进行动态采样,只能使用默认值,虽然默认值通常和实际的差别比较大:
http://docs.oracle.com/cd/B28359_01/server.111/b28274/stats.htm#i41866
3. 动态采样测试

create table t
as
select owner, object_type
from all_objects


  3.1通过hint禁用动态采样,采样级别设置为0即禁用动态采样

select /*+ dynamic_sampling(t 0) */* from t;


执行计划如下所示:

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |       |       |    63 (100)|          |
|   1 |  TABLE ACCESS FULL| T    | 18378 |   502K|    63   (0)| 00:00:01 |
--------------------------------------------------------------------------


优化器认为只会返回18378,实际t表中有80761条数据,和实际值的差别还是很大的。(不知道为什么如果建表语句为create table t as select * from all_objects的话,Rows的值会很接近实际值)

  3.2 假设我们不通过hint禁用动态采样,oracle10.1以上版本默认采样级别为2的动态采样

select * from t;


执行计划如下:

7    --------------------------------------------------------------------------
8    | Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
9    --------------------------------------------------------------------------
10    |   0 | SELECT STATEMENT  |      |       |       |    64 (100)|          |
11    |   1 |  TABLE ACCESS FULL| T    | 81761 |  2235K|    64   (2)| 00:00:01 |
12    --------------------------------------------------------------------------

38    Note
39    -----
40       - dynamic sampling used for this statement (level=2)


此时优化器的预估返回值81761和80761基本一致,3.1中的示例是优化器对cardinality的估值偏小,

同样存在因为缺乏统计信息导致优化器对cardinality估值偏大的情况

  3.3 对cardinality 估值偏大

我们对删除表t中的数据,然后再次执行3.1中的sql

delete from t;
commit;
select /*+ dynamic_sampling(t 0) */* from t;


得到的执行计划如下所示:

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |       |       |    63 (100)|          |
|   1 |  TABLE ACCESS FULL| T    | 18378 |   502K|    63   (0)| 00:00:01 |
--------------------------------------------------------------------------


和3.1基本没有变化,优化器错误的评估了cardinality。

4. 动态采样的使用情况

  4.1 访问一个刚被创建并导入大量数据且没有收集统计信息的表,比如系统期初上线通常会导入大批量数据,

如果没有来得及对表收集统计信息的话,动态采样就可以发挥作用帮助优化器走出相对合理的执行计划。

  4.2 临时表。临时表通常是没有统计信息的,动态采样可以帮助优化器确定临时表的信息。

5. 对关联列的动态采样

  抛开多列统计信息不谈,动态采样可以动态的收集表中列与列之间的关联关系,如果单纯用dbms_stats对表收集统计信息的话,

是不会知道表中列与列之间的关联关系的。

假设我们有员工信息表,表中有字段员工出生月份BIRTH_MONTH(如5月),星座CONS(如金牛座),一年有12月份,12个星座,

当我们想查询5月出生且星座为金牛座的员工时,可选择率似乎应该是1/12 * 1/12,但是通常情况5月出生的人一大部分都是金牛座。

当应用动态采样的时候,可以近似评估出正常的cardinality。

  5.1 动态采样对关联列评估出来的cardinality测试

drop table t;
create table t
as select decode( mod(rownum,2), 0, 'N', 'Y' ) flag1,
decode( mod(rownum,2), 0, 'Y', 'N' ) flag2, a.*
from all_objects a
create index t_idx on t(flag1,flag2);
--对表T收集统计信息
BEGIN
dbms_stats.gather_table_stats(USER,
'T',
method_opt => 'for all indexed columns size 254');
END;


此时表t中满足条件flag1='N'的数据为39016条,满足flag2='N'的数据也是39016,但是满足条件同时满足flag1='N'和flag2='N'的数据为0条

select * from t where flag1 = 'N' and flag2 = 'N';


执行计划如下所示:

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |       |       |   325 (100)|          |
|*  1 |  TABLE ACCESS FULL| T    | 19496 |  1941K|   325   (1)| 00:00:04 |
--------------------------------------------------------------------------


优化器错误的评估cardinality为19496,和实际值0差别巨大。

通过动态采样可以近似得到实际值:

SELECT /*+ dynamic_sampling(t 3) */
*
FROM t
WHERE flag1 = 'N'
AND flag2 = 'N';


执行计划如下所示:

8    -------------------------------------------------------------------------------------
9    | Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
10    -------------------------------------------------------------------------------------
11    |   0 | SELECT STATEMENT            |       |       |       |     2 (100)|          |
12    |   1 |  TABLE ACCESS BY INDEX ROWID| T     |     6 |   612 |     2   (0)| 00:00:01 |
13    |*  2 |   INDEX RANGE SCAN          | T_IDX |     6 |       |     1   (0)| 00:00:01 |
14    -------------------------------------------------------------------------------------

54    Note
55    -----
56       - dynamic sampling used for this statement (level=2)
57


评估出来的cardinality仅为6,和实际值0非常接近。(不知道为什么level=2,不应该是3吗?)

6.动态采样的采样级别
http://docs.oracle.com/cd/B19306_01/server.102/b14211/stats.htm#i43032
7.什么时候使用动态采样

  7.1 对OLAP或者数据仓库来说,SQL的执行时间远大于解析时间,建议将采样级别设置3或者4

  7.2 对OLTP系统来说,SQL的解析时间通常大于执行时间,会有很多执行时间很短的SQL,不建议修改默认的采样级别

  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: