oracle里的统计信息
1 oracle里的统计信息
Oracle的统计信息是这样的一组数据,存储在数据字典,从多个维度描述了oracle数据库对象的详细信息,有6种类型
表的统计信息:记录数、表块的数量、平均行长度等
索引的统计信息:索引的层级、叶子块的数量、聚簇因子等
列的统计信息:列的distinct、为null的数量、列的最小值、最大值及直方图信息
系统的统计信息:所在的数据库服务器的系统处理能力、包含cpu跟I/O
数据字典统计信息:数据库字典基表、表上的索引
内部对象统计信息:内部表X$系列表的详细信息
存放的系统数据字典
(1). DBA_TABLES
(2). DBA_OBJECT_TABLES
(3). DBA_TAB_STATISTICS
(4). DBA_TAB_COL_STATISTICS
(5). DBA_TAB_HISTOGRAMS
(6). DBA_INDEXES
(7). DBA_IND_STATISTICS
(8). DBA_CLUSTERS
(9). DBA_TAB_PARTITIONS
(10). DBA_TAB_SUBPARTITIONS
(11). DBA_IND_PARTITIONS
(12). DBA_IND_SUBPARTITIONS
(13). DBA_PART_COL_STATISTICS
(14). DBA_PART_HISTOGRAMS
(15). DBA_SUBPART_COL_STATISTICS
(16). DBA_SUBPART_HISTOGRAMS
2 oracle里收集与查看统计信息的方法
2.1收集统计信息
2种命令analyze跟dbms_stats包来收集
2.1.1 用analyze来收集统计信息
analyze table EMP compute statistics; SQL> create table t2 as select * from dba_objects; SQL> create index idx_t2 on t2(object_id); Index created. SQL> analyze index idx_t2 delete statistics; --删除该索引的统计信息 Index analyzed. SQL> select count(*) from t2; COUNT(*) ---------- 56007 SQL> analyze table t2 estimate statistics sample 15 percent for table; --以估算模式,采样比例为15%,现在只有表有统计信息,index跟列都还么有,这时的统计信息不准确(估算) Table analyzed. SQL> analyze table t2 compute statistics for table ; --计算表的真实统计信息 Table analyzed. SQL> analyze table t2 compute statistics for columns object_name,object_id; 对表t2 的2个列收集统计信息 Table analyzed. --对同一个对象而言新执行的analyze会抹去上一个analyze的结果 SQL> analyze table t2 compute statistics for table for columns object_name,object_id; --同时收集表和列的统计信息 Table analyzed. SQL> analyze index idx_t2 compute statistics; --收集该索引的统计信息 Index analyzed. SQL> analyze table t2 compute statistics; -一次收集表,列,索引的统计信息 Table analyzed.
2.2.2 用包来收集统计信息
EXEC dbms_stats.gather_table_stats(ownname => USER,tabname => 'EMP');
Oracle 10g以后创建index后,oracle会自动收集目标索引的统计信息
Dbms_stats包最常用的4个存储过程
gather_table_stats:收集目标表、列和表上的index的统计信息
gather_index_stats:收集指定index的统计信息
gather_schema_stats:收集指定schema下的所有对象的统计信息
exec dbms_stats.gather_schema_stats(OWNNAME=>'SOCIALDIAL',ESTIMATE_PERCENT=>100) gather_database_stats:收集全库对象的统计信息 exec dbms_stats.gather_database_stats(ESTIMATE_PERCENT=>100) exec dbms_stats.gather_table_stats(OWNNAME IN VARCHAR2, TABNAME IN VARCHAR2, PARTNAME IN VARCHAR2, ESTIMATE_PERCENT IN NUMBER, BLOCK_SAMPLE IN BOOLEAN, METHOD_OPT IN VARCHAR2, DEGREE IN NUMBER, GRANULARITY IN VARCHAR2, CASCADE IN BOOLEAN, STATTAB IN VARCHAR2, STATID IN VARCHAR2, STATOWN IN VARCHAR2,NO_INVALIDATE IN BOOLEAN, STATTYPE IN VARCHAR2, FORCE IN BOOLEAN
先删除t2表的统计信息
SQL> analyze table t2 delete statistics;
Table analyzed.
1 表
SQL> exec dbms_stats.gather_table_stats(OWNNAME=>'GRSV5',TABNAME=>'T2',ESTIMATE_PERCENT=>15, METHOD_OPT=>'FOR TABLE', CASCADE=> FALSE);
--估算模式,采用比例15%
PL/SQL procedure successfully completed.
修改为计算模式ESTIMATE_PERCENT=>100%或者null
2 指定列
METHOD_OPT:’for columns size 1 object_name,object_id’—对指定列收集
3 指定index
--收集指定index的统计信息
exec dbms_stats.gather_index_stats(OWNNAME=>'GRSV5', INDNAME=>'IDX_T2', ESTIMATE_PERCENT=>100);
4 删除
--删除表,列,索引的统计信息
exec dbms_stats.delete_table_stats(OWNNAME=>'GRSV5',TABNAME=>'T2');
2.1.3analyze跟dbms_stats包的区别
1 Analyze命令可以只收集列而不收集表的统计信息,dbms_stats做不到
2 Analyze命令不能正确的收集分区表的统计信息
3 Analyze命令不能并行收集统计信息
4 dbms_stats只能收集与CBO相关的统计信息
2.2 查看统计信息
2.2.1查看表的统计信息 SELECT NUM_ROWS, --表中的记录数 BLOCKS, --表中数据所占的数据块数 EMPTY_BLOCKS, --表中的空块数 AVG_SPACE, --数据块中平均的使用空间 CHAIN_CNT, --表中行连接和行迁移的数量 AVG_ROW_LEN --记录的平均长度 FROM USER_TABLES where table_name='DEPT'; 2.2.2 查看列的统计信息 SELECT TABLE_NAME,COLUMN_NAME, NUM_DISTINCT, --唯一值的个数 LOW_VALUE, --列上的最小值 HIGH_VALUE, --列上的最大值 DENSITY, --选择率因子(密度) NUM_NULLS, --空值的个数 NUM_BUCKETS, --直方图的BUCKET个数 HISTOGRAM --直方图的类型 FROM USER_TAB_COLUMNS where table_name='T2'; 2.2.3 查看index的统计信息 SELECT BLEVEL, --索引的层数 LEAF_BLOCKS, --叶子结点的个数 DISTINCT_KEYS, --唯一值的个数 AVG_LEAF_BLOCKS_PER_KEY, --每个KEY的平均叶块个数 AVG_DATA_BLOCKS_PER_KEY, --每个KEY的平均数据块个数 CLUSTERING_FACTOR --群集因子 FROM USER_INDEXES where index_name='IDX_T2';
3 表的统计信息
create table test (id number,name varchar2(10)); declare i number; begin for i in 1..10000 loop insert into test values(100,'hongquan'); end loop; commit; end ; select dump(100,16),dump('hongquan',16) from dual;
Typ=2 Len=2: c2,2 Typ=96 Len=8: 68,6f,6e,67,71,75,61,6e
--100,hongquan存储在db数据块的行占用字节+描述其长度的1字节
exec dbms_stats.gather_table_stats(OWNNAME=>'GRSV5',TABNAME=>'TEST',ESTIMATE_PERCENT=>100);
--收集表的统计信息
10000行记录一模一样,所有表的AVG_ROW_LEN=3+9
10000 28 0 0 0 12
--CBO会根据该统计信息来评估全部扫描的成本
统计信息的不准会导致sql性能问题
4 索引的统计信息
1 包含index的层级,叶子块的数量,聚簇因子等典型的维度
Blevel存储的是目标索引的层级(表示从根节点到目标叶子块的深度,用于cbo计算访问index 的成本),其值越大,访问的数据块就会越多,从0开始计算,
在oracle里,要降低目标index 的层级,可以通过rebuild来实现
analyze index IDX_T2 validate structure;
--分析索引结构
select name,height,lf_rows,lf_blks,del_lf_rows from index_stats where name='IDX_T2'
IDX_T2 2 56007 124 0
--删除表的大部分记录
delete from t2 where rownum<=55000
--再次分析并查询
IDX_T2 2 56007 124 55000
--再重新rebuildindex
alter index IDX_T2 rebuild;
--查询结果
IDX_T2 1 2 1 0
lf_blks:被CBO用于计算目标index做索引全扫描(index full scan)和索引范围扫描(index range scan)的成本
2 聚簇因子-clustering_factor
聚簇因子是指按照索引键值排序的索引行和存储对应表中数据行的存储顺序的相似程度(初始值为1)
Oracle首先定位到目标索引处于最左边的叶子块
从最左边的叶子块开始扫描,
聚簇因子高的索引走索引范围扫描比聚簇因子低的要耗费等多的物理io
在oracle中,能够降低目标索引的聚簇因子的唯一方法就是对表中数据按照目标索引的索引键值排序后重新存储
Cob计算索引范围扫描 cost=io cost + cpu cost
手动设置表的聚簇因子
exec dbms_stats.set_index_stats(OWNNAME =>'GRSV5',INDNAME =>'IDX_T2',clstfct =>10000,no_invalidate=>false);
5 列的统计信息
5.1 列的统计信息的含义和种类
Distinct、null、min、max
create table t2 as select * from dba_objects;
select count(*) from t2 where DATA_OBJECT_ID is null;
--对表t2 不收集直方图信息的统计信息
EXEC dbms_stats.gather_table_stats(ownname => 'GRSV5',tabname => 'T2',ESTIMATE_PERCENT=>100, METHOD_OPT=>'FOR ALL COLUMNS SIZE 1');
--查看其信息
SELECT * FROM DBA_TAB_COL_STATISTICS WHERE table_name='T2' AND COLUMN_NAME='DATA_OBJECT_ID';
5.2 列的统计信息不准导致谓词越界的实例
可以找到统计信息中的最大值跟最小值
exec dbms_stats.convert_row_value('',:temp);
输入的值是否在存在的值范围内,
--重新收集统计信息
5.3 直方图—histogram
Cbo默认目标列的min_value跟max_value是均匀分布的
例1
create table t2 (a number(5),b varchar2(5)); declare cnt number(5) :=1; begin loop insert into t2 values(1,1); if cnt =10000 then exit; end if; cnt:=cnt+1; end loop; insert into t2 values (2,2); commit; end; select b,count(*) from t2 group by b; create index idx_t2_b on t2(b); EXEC dbms_stats.gather_table_stats(ownname => 'GRSV5',tabname => 'T2',ESTIMATE_PERCENT=>100, METHOD_OPT=>'FOR ALL COLUMNS SIZE 1'); explain plan for select * from t2 where b='2'; select * from table(dbms_xplan.display); -------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 5001 | 25005 | 6 (0)| 00:00:01 | |* 1 | TABLE ACCESS FULL| T2 | 5001 | 25005 | 6 (0)| 00:00:01 |
--cbo 默认b列上均匀分布,b列只有2个值,返回的cardinality为5001
这时候需要收集直方图信息
--收集直方图信息
EXEC dbms_stats.gather_table_stats(ownname => 'GRSV5',tabname => 'T2',ESTIMATE_PERCENT=>100, METHOD_OPT=>'FOR COLUMNS SIZE 100 B'); ----- FOR COLUMNS SIZE AUTO B—对b列自动收集直方图信息 | 0 | SELECT STATEMENT | | 1 | 5 | 2 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| T2 | 1 | 5 | 2 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | IDX_T2_B | 1 | | 1 (0)| 00:00:01 |
1 直方图的种类
Frequence:直方图的bucket的数量就等于目标列的distinct值的数量
Frequence类型的直方图所对应的bucket的数量不能超过254(12c中取消)
SELECT * FROM DBA_TAB_COL_STATISTICS WHERE table_name='T2' AND COLUMN_NAME='B';
Height balanced直方图:相对而言,可以收集大于254行的distinct
5.3 直方图的收集方法
在包dbms_stats中的method_opt参数来指定
Method_opt参数:
For all [index |hidden |columns [size_clause]---所有列
For columns [size_clause] column|attribute ----部分列
Size格式:1-254范围的整数(1表示删除该目标的直方图信息)
Repeat只对已经有直方图统计信息的列收集
Auto让oracle自行决定是否对目标列收集
Skewonly只对数据分布不均匀的列收集
直方图的影响:对cbo估算可选择率,进而对计算成本、选择执行计划有直接影响
5.4 只用直方图的注意事项
1 直方图是专门为了分布不均匀的目标列的可选择率吗,如果分布均匀,或者有pk,unique根本不需要收集直方图信息
2 对于那些没有在where条件中出现的列,不管是否均匀,都不需要收集
3直方图统计信息可鞥会影响shared cursor的共享
4主要收集策略
6全局统计信息
全局统计信息对分区表、分区索引才有意义
针对分区表的sql,除非其查询条件限定了待访问的数据只在部分分区上,否则在大多数情况下都需要全局统计信息才能 正确的执行计划
Oracle的全局统计信息只能通过dbms_stats包来收集(参数:granularity来指定全局统计信息的统计策略,
Granularity:global(表)、partition(分区)、subpartition(子分区)、global and partition(表+分区)、all、auto
Auto是默认值,oracle会根据分区表的类型来决定统计信息的收集级别
select DBMS_STATS.GET_PARAM('granularity') from dual;
AUTO—查看其默认值
dbms_stats.delete_table_stats—可以删除分区表上的三个级别的统计信息
7 动态采样
作用:1oracle使用了动态采样,不管sql语句各列有什么的关联关系,大多数情况下,cbo都能相对正确的估算整个where条件的组个可选择率
2 一定程度上解决临时表没有统计信息而导致cbo错选择执行计划
动态采样仅适合于那些针对单表或者多表关联的第一个驱动表的select、update、delete语句
参数optimizer_dynamic_sampling的值>=1,10g默认是2
8多列统计信息(11g)
Method_opt“for columns(n1,n2) size auto”
9系统统计信息
用来描述oracle数据库所在服务器的系统处理能力的辅助统计信息,cpu的主频、单读块的平均消耗时间、多快读的平均消耗时间和单次多快读所能读取的数据块的平均值
dbms_stats.gather_system_stats('start');
dbms_stats.gather_system_stats('stop');
--系统统计信息在表AUX_STATS$
select * from sys.AUX_STATS$
数据字典、内部对象的统计信息X$表
10 oracle里的自动收集统计信息
Oracle 10g开始引入自动收集统计信息作业,能够每天自动收集统计信息,该作业会判断:1 需要对那些对象收集统计信息,2 需要对哪些统计信息已经过期的对象重复收集统计信息,3以估算模式收集统计信息的采样比例,4 需要对哪些列收集直方图信息,5是否启用并行收集以及相应的并行度
参数statistics_level的值为typical或者all,
select job_name,program_name,schedule_name from dba_scheduler_jobs where job_name='GATHER_STATS_JOB'
GATHER_STATS_JOB GATHER_STATS_PROG MAINTENANCE_WINDOW_GROUP
10g 中自动统计信息作业可配置维护的窗口
select * from dba_scheduler_wingroup_members where window_group_name='MAINTENANCE_WINDOW_GROUP'
MAINTENANCE_WINDOW_GROUP WEEKNIGHT_WINDOW
MAINTENANCE_WINDOW_GROUP WEEKEND_WINDOW
--查询近几次收集记录
select * from (select* from dba_scheduler_job_run_details where job_name='GATHER_STATS_JOB' order by log_date desc ) where rownum<=4;
----10g中的该job只有2个可维护的窗口,可以无限制的消耗系统资源
----11g中取消了该job,引用GATHER_STATS_PROG
Insert+delete+update所影响的记录超过tab$中记录表的记录数的10%,或者目标表执行过truncate操作,oracle自认为该表的统计信息失效,自动收集该表的统计信息
10g中禁用job 来收集
exec dbms_scheduler.disable9\('GATHER_STATS_JOB')
锁定与解锁表的统计信息
analyze table IDD_CHANNEL compute statistics for table for all indexes;
ORA-38029: object statistics are locked
--解锁表的统计信息
exec dbms_stats.unlock_table_stats(ownname=>'CLOUDAPI',tabname=>'IDD_CHANNEL');
--锁定表的统计信息
exec dbms_stats.lock_table_stats(ownname=>'CLOUDAPI',tabname=>'IDD_CHANNEL');
schema 针对用户