深入理解 Oracle 动态采样

动态采样介绍

Oracle 动态采样(Dynamic Sampling,12c 称为 Dynamic statistics),是对统计信息的⼀个重要补充。

有这样一种业务场景,当数据动态变化,无法用典型的统计信息描述时,怎么保证表的统计信息是准确的?

这时候动态采样就可以派上用场了。动态采样可以给在解析时对表中数据进行采样,为优化器器提供准确的估算值(cardinality)。

动态采样的主要有以下几个应用场景:

一个经典的场景就是业务场景中的临时表,ETL数据清洗转换过程中或者BI 系统中存放计算报表都会用到临时表。

这些临时表可能是 Oracle 中的 global temporary table,也可能是正常的堆表。

临时表中的数据时动态变化的,数据量在不同时间点变化很大,这就会对表的统计信息收集造成很大影响,没有一种合适的统计信息使优化器产生合适的执行计划。

这种场景适合采用动态采样技术,删除临时表上的统计信息,并且锁定统计信息,不让搜集统计信息的Job更新临时表上的统计信息,查询临时表时,优化器会对临时表进行动态采样,以确定临时表的 cardinality。

另一个场景是在单表上使用组合过滤条件,并且组合过滤条件并不是简单的相等操作,或者在过滤列上使用转换函数,无法使用 column group 扩展统计信息,简单使用多个列上的统计信息也无法产生合适的统计信息。

12c 之前,动态采样只能预估单表 cardinality,12c 版本,Oracle 对动态采样做了很大的增强,可以估算 group by 的聚合结果集和连接结果集的 cardinality。

使用动态采样,优化器往往可以获得高质量的估算值,从⽽而产生更优化的执行计划。


本文将介绍三种动态采样的适用场景。

1、临时表和动态采样,优化器器动态采样解析

以笔者优化过的一家客户CRM系统为例:

分析语句执行时间经常需要5分钟以上,通过分析SQL的执行计划,发现执行计划的估算值偏差离谱。

比如SQL Monitor 报告中,对于表A优化器器估算值为1,表连接方式采用nested loop最佳,实际执行计划也是nested loop ,但表实际数据量为一千万行,被驱动表被访问了一千万次。

通过查看表的统计信息,可以发现搜集统计信息时,表A中没有数据, Num_Rows 为0行, 所以优化器器估算为1行。

虽然A是正常堆表,但是在业务中做临时表使用,数据是动态生成和删除的。

解决方案:

删除表A统计信息并且进行锁定,保证后续对临时表的查询会使用动态采样,得到准确的估算值。
Exec dbms_stats.delete_table_stats(‘CRM’,’A’);
Exec dbms_stats.lock_table_stats(‘CRM’,’A’);

优化后再执行结果秒出。

2、复杂查询的动态采样

对于有复杂的过滤条件的sql, 为了在执行计划中得到正确的cardinality, 统计信息未必有帮助, 包括extended statistics. 

比如下面in和like的组合条件, 或者where条件中使用了自定义的函数.
status in (‘COM’, ‘ERR’) and v1 like ‘10%’

这时候dynamic sampling可能是唯一的选择. 下面是一个例子, 采用level为6的采样之后, cardinality更更为接近真实的数据.

构造一个1万行数据的测试表,搜集统计信息。
create table t1 as
with v1 as (
select /*+ materialize */
rownum id from dual connect by level <= 1000
)
select
rownum id,
rpad(rownum, 10, '0') v1,
trunc((rownum - 1)/100) n1,
case
when mod(rownum,100000) = 7 then 'ERR'
when rownum <= 9990000 then 'COM'
when mod(rownum,10) =0 then 'NEW'
when mod(rownum,10) between 1 and 5 then 'PRP'
when mod(rownum,10) between 6 and 8 then 'FKC'
when mod(rownum,10) = 9 then 'LDD'
end status,
rpad(rownum, 100) padding
from v1, v1
where rownum <= 1e6;
begin
dbms_stats.gather_table_stats(user,'t1');
end;
/

测试 SQL,估算值为395行,实际值为11113行,差距为30倍左右。

select
count(*)
from
t1
where
status in ('COM', 'ERR')
* and v1 like '10%'

SQL> /
COUNT(*)
----------
11113
Execution Plan
----------------------------------------------------------
Plan hash value: 4096694858
----------------------------------------------------------------------------------
-
| Id | Operation                | Name | Rows | Bytes | Cost (%CPU)| Time
|
----------------------------------------------------------------------------------
-
| 0  | SELECT STATEMENT         |      | 1    | 15    | 4983 (1)   | 00:00:01
|
| 1  | SORT AGGREGATE           |      | 1    | 15    |            |
|
|* 2 | TABLE ACCESS STORAGE FULL| t1   | 395 | 5925   | 4983 (1)   | 00:00:01
|
----------------------------------------------------------------------------------
-
Predicate Information (identified by operation id):
---------------------------------------------------
2 - storage("V1" LIKE '10%' AND ("STATUS"='COM' OR "STATUS"='ERR'))
filter("V1" LIKE '10%' AND ("STATUS"='COM' OR "STATUS"='ERR'))

使用动态采样,级别为6,估算值为16595行,实际为11113行,差距不到2倍,估算值的质量大幅提升。

SQL> select /*+ OPT_PARAM('OPTIMIZER_DYNAMIC_SAMPLING', 6) */
count(*)
from
t1
where
status in ('COM', 'ERR')
and v1 like '10%';
COUNT(*)
----------
11113
Execution Plan
----------------------------------------------------------
Plan hash value: 4096694858
----------------------------------------------------------------------------------
-
| Id | Operation                | Name | Rows  | Bytes | Cost (%CPU)| Time
|
----------------------------------------------------------------------------------
-
| 0  | SELECT STATEMENT         |      | 1     | 15    | 4983 (1)| 00:00:01
|
| 1  | SORT AGGREGATE           |      | 1     | 15    |         |
|
|* 2 | TABLE ACCESS STORAGE FULL| t1   | 16595 | 243K  | 4983 (1)| 00:00:01
|
----------------------------------------------------------------------------------
-
Predicate Information (identified by operation id):
---------------------------------------------------
2 - storage("V1" LIKE '10%' AND ("STATUS"='COM' OR "STATUS"='ERR'))
filter("V1" LIKE '10%' AND ("STATUS"='COM' OR "STATUS"='ERR'))
Note
-----
- dynamic statistics used: dynamic sampling (level=6)

3、12c 动态采样的增强,对连接和 group by 结果集的统计

测试SQL结果集为13行,12c中采样级别设为11,实际为auto时,CBO估算为12⾏,准确性很高,并且在表上有统计信息的情况下依然可以进行采样(采样级别设为6时,

不使用采样,清除表上的统计信息后,可发现采12c 动态采样的增强,对连接和 group by 结果集的统计样级别为6的时候,CBO估算值15743行,差别很大)

11G 版本采样级别为6时与12c 相同(注:动态采样的级别对于信息收集的准确度会有一定的影响,当然也会消耗额外的资源)。

总结

Oracle 动态采样在性能优化上有诸多应用场景,12c中更是得到加强,更深⼊入的了解动态采样的特性对性能优化有着重要的意义。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值