一、问题
- Oracle在收集统计信息时默认的采样比例是DBMS_STATS.AUTO_SAMPLE_SIZE,那么AUTO_SAMPLE_SIZE的值具体是多少?
- 假设采样比例为10%,那么在计算单个列的distinct时与实际的差别大吗?
- 有哪些采样算法?
SQL> begin2dbms_stats.gather_table_stats(3ownname => 'BAO',4tabname => 'T1',5estimate_percent => 1006);7end;8/PL/SQL procedure successfully completed.SQL> begin2dbms_stats.gather_table_stats(3ownname => 'BAO',4tabname => 'T2',5estimate_percent => 106);7end;8/PL/SQL procedure successfully completed.SQL> begin2dbms_stats.gather_table_stats(3ownname => 'BAO',4tabname => 'T3',5estimate_percent => dbms_stats.auto_sample_size6);7end;8/PL/SQL procedure successfully completed.
查看这三张表的统计信息 , 可以看到采用100%和AUTO_SAMPLE_SIZE这两种方式收集的统计信息的SAMPLE_SIZE相同,都是全量收集 。SQL> select table_name, num_rows, sample_size from user_tables where table_name in ('T1', 'T2', 'T3');TABLE_NAMENUM_ROWSSAMPLE_SIZE-----------------------------------T1145334145334T214619014619T3145334145334
官方文档并没有说明AUTO_SAMPLE_SIZE具体的值是多少,但是从实验结果来看,这个值就是100 。这就回答了文章的第一个问题 。Oracle为什么会默认采用100%的方式来收集统计信息呢,在ASKTOM有同行就提出过这个问题“DBMS_STATS.AUTO_SAMPLE_SIZE seems to always generate 100%”,他们的回复是为了得到精确的distinct列值 。接下来我们就来看下全量采集和部分采集列的distinct区别 。
SQL> select a.column_name, a.num_distinct "t1.num_distinct", b.num_distinct "t2.num_distinct",2round((a.num_distinct - b.num_distinct) * 100 / a.num_distinct, 1) "diff",3a.sample_size "t1.sample_size", b.sample_size "t2.sample_size"4from (select table_name, column_name, num_distinct, sample_size from user_tab_col_statistics where table_name in ('T1')) a,5(select table_name, column_name, num_distinct, sample_size from user_tab_col_statistics where table_name in ('T2')) b6where a.column_name = b.column_name and a.num_distinct > 0 order by "diff" desc;COLUMN_NAMEt1.num_distinct t2.num_distinctdiff t1.sample_size t2.sample_size------------------------------ --------------- --------------- ---------- -------------- --------------OBJECT_NAME64552103008414533414619SUBOBJECT_NAME101538562.1682516856TIMESTAMP258512405214521214610LAST_DDL_TIME2490125749.514521214610CREATED2312120947.714533414619NAMESPACE211528.614521214610OBJECT_TYPE453913.314533414619OWNER807111.314533414619TEMPORARY22014533414619DUPLICATED11014533414619STATUS22014533414619SHARDED11014533414619GENERATED22014533414619SECONDARY11014533414619SHARING44014533414619EDITIONABLE220254332531ORACLE_MAINTAINED22014533414619APPLICATION11014533414619DEFAULT_COLLATION110168861705DATA_OBJECT_ID7778578100-.4778227813OBJECT_ID145212146100-.614521214610
T1表是全量收集,T2表是按10%的比例收集,从上面的结果可以看到,对于大部分字段通过部分采样的方式都能估算得很准确 。但对于OBJECT_NAME这个列 , 估算出来的值和全量统计的差别很大,我们来看一下是什么原因导致的 。SQL> select count(*), object_name from t1 group by object_name order by count(*) desc;COUNT(*) OBJECT_NAME---------- -----------------690 S_AAA_CCD690 S_ABA_CED690 S_ACA_CCD690 S_ADA_CCD690 PK_AEA_CED...1 GV_$CON_SYSSTAT1 GV_$DATAFILE1 GV_$TABLESPACE1 GV_$ROLLSTAT1 GV_$PARAMETER
【Oracle收集统计信息的一些思考】可以看到OBJECT_NAME这个列的数据分布极不均匀 。因此对于分布不均匀的列,通过部分采样方式得到的distinct值与实际的distinct值差别就会比较大 。这就回答了文章的第二个问题 。三、采样算法以下是个人的一些娱乐性思考
- 等比放大,即(采样得到distinct值 / 采样行数) x 总行数 。举个例子,假设表有1000行数据,只采样100行,A列有95个不同的值,即count(distinct A) / count(A) = 95% , 那么等比放大很容易推导出1000行数据,有950个A的不同值 。但是如果这100行中B列只有2个不同的值,即count(distinct B) / count(B) = 2% , 那么对于1000行的表来讲,B的不同值是不是等于2% * 1000呢?很有可能不是,说不定全表就这两个不同值,例如性别 。所以通过等比放大得到的distinct值就不准 。这种算法有明显的缺陷 。
- 按增长率估算,即将采样得到的前5%作为一个基数 , 采样得到的后5%作为一个增长率 。(假设采样比例是10%)还是举个例子,假设表有1000行数据,只采样100行 , 采样的前50行,C列有40个不同值 。采样的后50行 , C列又多了30个不同值,即总共有70个不同值 。那么后面的90%都会保持这个增长速度 。则总体的C列不同值为40 + 30 * ((100-5)/5) = 610 。再来看一种情况,假设采样的前50行,D列有2个不同值 。采样的后50行,D列多了0个不同值,即不同值总数保持不变 。那么后面的90%都会保持这个增长速度 。则总体的D列不同值仍为2 。这种方式似乎比等比采样更加合乎实际情况一点 。接下来就用python去实现这个算法,看看与oracle的估算差别有多大 。以下为python代码 。
推荐阅读
- 纸嫁衣4红丝缠公交车统计表怎么解迷
- 1分钟完成在线测试部署便捷收集班级同学文件的web管理系统
- 在CentOS7下安装Oracle11教程
- 图文超详解 G1 垃圾收集器深入剖析
- Oracle中查询表结构的六种方法
- 4 探究Presto SQL引擎-统计计数
- 原神证悟木怎么快速收集
- 一 网络安全:信息收集之玩转nmap(理论篇)
- Oracle数据库的两种授权收费方式介绍!
- 使用LEFT JOIN 统计左右存在的数据