统计信息对基于CBO优化器的Oracle版本尤为重要,一个统计信息的好坏,很大程度将直接影响原本有可能几秒执行成功的一段SQL脚本,因为错误的统计信息而选择一条错误的执行计划,导致几分钟才能出来结果。
Oracle中的优化器是SQL分析和执行的优化工具,它负责生成、制定SQL的执行计划。
Oracle 中主要的优化器有两种:
RBO有严格的使用规则,只要按照这套规则(Oracle制定)去写SQL语句,无论数据表中的内容怎样,也不会影响到你的执行计划;
换句话说,RBO对数据“不敏感”,它要求SQL编写人员必须要了解各项细则;
RBO一直沿用至ORACLE 9i,从ORACLE 10g开始,RBO已经彻底被抛弃。
CBO是一种比RBO更加合理、可靠的优化器,在ORACLE 10g中完全取代RBO;
CBO通过计算各种可能的执行计划的“代价”,即COST,从中选用COST最低的执行方案作为实际运行方案;
它依赖数据库对象的统计信息,统计信息的准确与否会影响CBO做出最优的选择,也就是对数据“敏感”。
执行计划中的 Cost的计算方式默认为CPU+I/O两者之和。 所以一般我们看执行计划是,cost 越低,SQL 的性能就越好。
Number of rows(行数)
Number of blocks(块数)
Average row length(行平均长度)
Number of distinct values (NDV) in column(列中不同值的数量)
Number of nulls in column(列中null的数量)
Data distribution (histogram)(数据分布(柱状图/直方图))
Number of leaf blocks(叶子块的数量)
Levels(索引的高度)
Clustering factor(聚簇因子)
I/O performance and utilization(I/O性能和利用)
CPU performance and utilization(CPU性能和利用)
序号 |
列名 |
解释 |
1 |
recursive calls |
递归调用,表示执行SQL的时候的产生的递归调用的次数。Oracle在执行SQL的时候,有时候会生成很多额外的SQL语句,这个就称为递归调用。这个参数和访问数据字典的次数有很大的关系,一般来说,这个参数值不会很大。 |
2 |
db block gets |
DB块取,表示当前读。在发生INSERT、DELETE、UPDATE和SELECT FOR UPDATE的时候,数据库缓冲区中的数据库块的个数。在SELECT语句中一般为0。 |
3 |
consistent gets |
一致性读,表示除了SELECT FOR UPDATE的时候,从数据库缓冲区中读取的数据块的个数(注意,实际上并不是块的个数),可能会读取回滚段的信息,一般来说,逻辑读(Logical Reads) = 当前读(db block gets) + 一致性读(consistent gets)。 |
4 |
physical reads |
物理读,在执行SQL的过程中,从硬盘上读取的数据块个数。 |
5 |
redo size |
SQL语句在执行过程中产生的Redo的字节数。 |
6 |
bytes sent via SQL*Net to client |
服务器利用SQL*Net发送到客户端的字节数。 |
7 |
bytes received via SQL*Net from client |
服务器利用SQL*Net从客户端接收的字节数。 |
8 |
SQL*Net roundtrips to/from client |
从客户端发送和接收的SQL*Net消息的总数,包括从多行的结果集中提取的往返消息。 |
9 |
sorts (memory) |
在内存执行的排序次数。 |
10 |
sorts (disk) |
在磁盘上执行的排序次数,如果内存空间不足,那么会使用磁盘空间。 |
11 |
rows processed |
更改或选择返回的行数。 |
找到篇文章,统计信息的步骤,包含表名https://www.cnblogs.com/bicewow/p/11263378.html
Oracle 的Automatic Statistics Gathering 是通过Scheduler 来实现收集和维护的。名称为GATHER_STATS_JOB的Job专门用来收集两种信息 :
(1)Missing statistics(统计信息缺失),(2)Stale statistics(统计信息陈旧)
该Job 是在数据库创建的时候自动创建,并由Scheduler来管理。Scheduler 在maintenance windows open时运行gather job。 默认情况下,job 会在每天晚上10到早上6点和周末全天开启。该过程首先检测统计信息缺失和陈旧的对象。然后确定优先级,再开始进行统计信息。
Scheduler Job的 stop_on_window_close 属性控制GATHER_STATS_JOB 是否继续。该属性默认值为True. 如果该值设置为False,那么GATHER_STATS_JOB 会中断, 而没有收集完的对象将在下次启动时继续收集。
Gather_stats_job 调用dbms_stats.gather_database_stats_job_proc过程来收集statistics 的信息。 该过程收集对象statistics的条件如下:
(1)对象的统计信息之前没有收集过。
(2)当对象有超过10%的rows 被修改,此时对象的统计信息也称为stale statistics。
查看任务详情
SELECT owner,job_name,state,last_start_date,last_run_duration,failure_count FROM dba_scheduler_jobs WHERE job_name = 'GATHER_STATS_JOB';
select PROGRAM_ACTION from dba_scheduler_programs where PROGRAM_NAME = 'GATHER_STATS_PROG';
任务执行历史
SELECT log_id, job_name, status,TO_CHAR (log_date, 'DD-MON-YYYY HH24:MI') log_date FROM dba_scheduler_job_run_details WHERE job_name = 'GATHER_STATS_JOB';
停止Job
在sysdba下执行: exec DBMS_SCHEDULER.DISABLE('GATHER_STATS_JOB');
或
BEGIN
DBMS_SCHEDULER.DISABLE('GATHER_STATS_JOB');
END;
补充点:oracle是如何知道修改超过10%的?
SGA的shared pool存有SQL的statistics情况,对应的有SQL处理的行数(insert,delete,truncate,update等),SMON进程定期将这些信息刷到表SYS.MON_MODS$基表中,SMON后台进程会每15分钟将SGA中的DML统计信息刷新到SYS.MON_MODS$基表中。同时会将SYS.MON_MODS$中符合要求的数据MERGE合并到MON_MODS_ALL$中,并清空原MON_MODS$中的数据。
MON_MODS_ALL$作为dba_tab_modifications视图的数据来源,起到辅助统计信息收集的作用。这样基于之前的统计信息中的表的行数(dba_tables.num_rows),对比 MON_MODS_ALL$(dba_tab_modifications)中的update、delete、insert、truncate信息就可以知道该表从上一次收集统计信息到现在做了多少百分比的修改,若该百分比超过10%则判定为stale陈旧,否则为不陈旧。陈旧的统计信息会在自动收集统计信息时再次被收集。
对MON_MODS$字典有兴趣的可以参考https://www.askmaclean.com/archives/smon-flush-dml-statistics-mon-mods.html
STATISTIC_LEVEL 参数是oracle9.2开始引入的一个控制系统统计参数收集的一个开关.一共有三个值:basic,typical,all,来决定是否对对象进行监控。支持alter session,alter system 动态修改.如果要用statspack或者AWR收集系统性能统计数据.那么这个参数的值必须为typical或all.通常all是一个全面收集,包括 OS以及sql执行路径方面的一些统计信息,除非遇见非常严重的性能问题或在一些特殊的性能挣断方面才会用到STATISTICS_LEVEL=all, 平常STATISTICS_LEVEL=typical已经足够诊断99%的性能问题了.
通过设置初始化参数 STATISTIC_LEVEL 为 TYPICAL 或 ALL,就可以自动收集统计信息(默认值为TYPICAL),STATISTIC_LEVEL 参数的值可以激活GATHER_STATS_JOB。
example: seting statistics_level
alter system set statistics_level=basic;
alter system set statistics_level=typical;
alter system set statistics_level=all;
or
alter session set statistics_level=basic;
alter session set statistics_level=typical;
alter session set statistics_level=all;
statistics_level=basic的情况下,oracle关闭了所有性能数据的收集,也就是如果要关闭AWR或statspack收集,只要设置alter system set statistics_level=basic;就行了;
statistics_level=typical的时候,除了plan_executetion_statistics和OS Statistics不能收集外,其他的都可以收集,如要要收集这个两项,必须设置statistics_level=all;
如果当statistics_level=ALL,系统收集所有的统计信息.
查看当前状态:show parameter statistics_level
如前面所述,如果新建一张表或操作,Oracle是不会立即去执行分析的,因此有了动态采样来辅助做分析。
动态采样(Dynamic Sampling)技术的最初提出是在Oracle 9i R2,在段(表,索引,分区)没有分析的情况下,为了使CBO 优化器得到足够的信息以保证做出正确的执行计划而发明的一种技术,可以把它看做分析手段的一种补充。
当段对象没有统计信息时(即没有做分析),动态采样技术可以通过直接从需要分析的对象上收集数据块(采样)来获得CBO需要的统计信息。
级别 | 描述 |
---|---|
Level 0 | 不做动态分析 |
Level 1 | Oracle 对没有分析的表进行动态采样,但需要同时满足以下4个条件。 (1) SQL中至少有一个未分析的表 (2)未分析的表出现在关联查询或者子查询中 (3)未分析的表没有索引 (4)未分析的表占用的数据块要大于动态采样默认的数据块(32个) |
Level 2 | 对所有的未分析表做分析,动态采样的数据块是默认数据块的2倍。 |
Level 3 | 采样的表包含满足Level 2定义的所有表,同时包括,那些谓词有可能潜在地需要动态采样的表,这些动态采样的数据块为默认数据块,对没有分析的表,动态采样的默认块为默认数据块的2倍。 |
Level 4 | 采样的表包含满足Level 3定义的表,同时还包括一些表,他们包含一个单表的谓词会引用另外的2个列或者更多的列;采样的块数是动态采样默认数据块数;对没有分析的表,动态采样的数据块为默认数据块的2倍。 |
Level 5-9 | 采样的表包含满足Level 4定义的表,同时分别使用动态采样默认数据块的2,4,8,32,128 倍的数量来做动态分析。 |
Level 10 | 采样的表包含满足Level 9定义的所有表,同时对表的所有数据进行动态采样。 |
采样的数据块越多,得到的分析数据就越接近与真实,但同时伴随着资源消耗的也越大。
设置hint不动态采集信息
select /*+dynamic_sampling(t 0) */ * from tablename;
动态采样有两方面的作用:
(1)CBO 依赖的是充分的统计分析信息,但是并不是每个用户都会非常认真,及时地去对每个表做分析。 为了保证执行计划都尽可能地正确,Oracle 需要使用动态采样技术来帮助CBO 获取尽可能多的信息。
(2)全局临时表。 通常来讲,临时表的数据是不做分析的,因为它存放的数据是临时性的,可能很快就释放了,但是当一个查询关联到这样的临时表时,CBO要想获得临时表上的统计信息分析数据,就只能依赖于动态采样了。
动态采样除了可以在段对象没有分析时,给CBO提供分析数据之外,还有一个独特的能力,它可以对不同列之间的相关性做统计。
相对的,表分析的信息是独立的。 如:
(1)表的行数,平均行长。
(2)表的每个列的最大值,最小值,重复率,也可能包含直方图。
(3)索引的聚合因子,索引叶的块数目,索引的高度等。
动态采样的缺点
(1)采样的数据块有限,对于海量数据的表,结果难免有偏差。
(2)采样会消耗系统资源,特别是OLTP数据库,尤其不推荐使用动态采样
动态采样摘自https://www.cndba.cn/Dave/article/1575
analyze table tt1 estimate statistics sample 15 percent for table --估算模式收集表统计信息
analyze table tt1 compute statistics for table --计算模式收集表统计信息
analyze table tt1 comupte statistics for column object_id --对指定列收集统计信息
analyze table tt1 comupte statistics for table for column object_id --对指定列和表收集统计信息
analyze index idx_tt1 compute statistics --对指定索引收集统计信息
analyze table tt1 delete statistics -- 对指定表删除所有统计信息
analyze table tt1 compute statistics --对指定表收集所有统计信息
收集步骤
--创建统计信息历史保留表
sql> exec dbms_stats.create_stat_table(ownname => 'scott',stattab => 'stat_table') ;
--导出整个scheme的统计信息
sql> exec dbms_stats.export_schema_stats(ownname => 'scott',stattab => 'stat_table') ;
--分析scheme
Exec dbms_stats.gather_schema_stats(
ownname => 'scott',
options => 'GATHER AUTO',
estimate_percent => dbms_stats.auto_sample_size,
method_opt => 'for all indexed columns ',
degree => 6 )
--分析表
sql> exec dbms_stats.gather_table_stats(ownname => 'scott',tabname => 'work_list',estimate_percent => 10,method_opt=> 'for all indexed columns') ;
--分析索引
SQL> exec dbms_stats.gather_index_stats(ownname => 'crm2',indname => 'IDX_ADM_PERMISSION_PID_MID',estimate_percent => '10',degree => '4') ;
--如果发现执行计划走错,删除表的统计信息
SQL>dbms_stats.delete_table_stats(ownname => 'scott',tabname => 'work_list') ;
--导入表的历史统计信息
sql> exec dbms_stats.import_table_stats(ownname => 'scott',tabname => 'work_list',stattab => 'stat_table') ;
--如果进行分析后,大部分表的执行计划都走错,需要导回整个scheme的统计信息
sql> exec dbms_stats.import_schema_stats(ownname => 'scott',stattab => 'stat_table');
--导入索引的统计信息
SQL> exec dbms_stats.import_index_stats(ownname => 'crm2',indname => 'IDX_ADM_PERMISSION_PID_MID',stattab => 'stat_table')
--检查是否导入成功
SQL> select table_name,num_rows,a.blocks,a.last_analyzed from all_tables a where a.table_name='WORK_LIST';
分析数据库(包括所有的用户对象和系统对象):gather_database_stats
分析用户所有的对象(包括表、索引、簇):gather_schema_stats
分析表:gather_table_stats
分析索引:gather_index_stats
删除数据库统计信息:delete_database_stats
删除用户方案统计信息:delete_schema_stats
删除表统计信息:delete_table_stats
删除索引统计信息:delete_index_stats
删除列统计信息:delete_column_stats
设置表统计信息:set_table_stats
设置索引统计信息:set_index_stats
设置列统计信息:set_column_stats
可以查看表 DBA_TABLES来查看表是否与被分析过,如:
SELECT TABLE_NAME, LAST_ANALYZED FROM DBA_TABLES
一些其他应用
--估算模式对制定表上收集统计信息
exec dbms_stats.gather_table_stats(ownname => 'test',tabname => 'tt1',estimate_percent => 15,method_opt => 'for table',cascade => false);
--计算方式对制定表上收集统计信息
exec dbms_stats.gather_table_stats(ownname => 'test',tabname => 'tt1',estimate_percent => 100,method_opt => 'for table',cascade => false);
--计算方式对指定表列上收集统计信息
exec dbms_stats.gather_table_stats(ownname => 'test',tabname => 'tt1',estimate_percent => 100,method_opt => 'for columns size 1 object_name object_id',cascade => false);
--指定模式下索引收集统计
exec dbms_stats.gather_index_stats(ownname => 'test',indname => 'idx_tt1',estimate_percent => 100);
--删除表统计信息
exec dbms_stats.delete_table_stats(ownname => 'test',tabname => 'tt1');
--一次性收集表统计信息
exec dbms_stats.gather_table_stats(ownname => 'test',tabname => 'tt1',estimate_percent => 100,cascade => true);
DBMS_STATS.GATHER_TABLE_STATS 字段解释
字段名 | 解释 |
---|---|
ownname | 要分析表的拥有者 |
tabname | 要分析的表名 |
partname | 分区的名字,只对分区表或分区索引有用. |
estimate_percent | 采样行的百分比,取值范围[0.000001,100],null为全部分析,不采样 常量:DBMS_STATS.AUTO_SAMPLE_SIZE是默认值,由oracle决定最佳取采样值. |
block_sample | 是否用块采样代替行采样. |
method_opt | 决定histograms信息是怎样被统计的.method_opt的取值如下(默认值为FOR ALLCOLUMNS SIZE AUTO): 1、for all columns:统计所有列的histograms 2、for all indexedcolumns:统计所有indexed列的histograms. 3、for all hiddencolumns:统计你看不到列的histograms 4、for columns REPEAT上次统计过的histograms;AUTO由oracle决定N的大小; SKEWONLY multipleend-points with the same value which is what we define by "there isskew in thedata |
degree | 决定并行度.默认值为null |
granularity | 要收集的统计数据的粒度,只有在表被分区时才相关 |
cascade | 是收集索引的信息.默认为FALSE. |
stattab | 指定要存储统计信息的表,statid如果多个表的统计信息存储在同一个stattab中用于进行区分.statown存储统计信息表的拥有者.以上三个参数若不指定,统计信息会直接更新到数据字典. |
no_invalidate | 如果将依赖游标设置为TRUE,则不使其无效。如果将依赖游标设置为FALSE,则该过程将立即使依赖游标无效。 |
force | 即使表锁住了也收集统计信息. |
举例:
execute dbms_stats.gather_table_stats(ownname => 'owner',tabname => 'table_name' ,estimate_percent => null ,method_opt => 'for all indexed columns' ,cascade => true);
1、对于分区表,建议使用DBMS_STATS,而不是使用Analyze语句。
a) 可以并行进行,对多个用户,多个Table
b) 可以得到整个分区表的数据和单个分区的数据。
c) 可以在不同级别上Compute Statistics:单个分区,子分区,全表,所有分区
d) 可以倒出统计信息
e) 可以用户自动收集统计信息
2、DBMS_STATS的缺点
a) 不能Validate Structure
b) 不能收集CHAINED ROWS, 不能收集CLUSTER TABLE的信息,这两个仍旧需要使用Analyze语句。
c) DBMS_STATS 默认不对索引进行Analyze,因为默认Cascade是False,需要手工指定为True
3、对于oracle 9里面的External Table,Analyze不能使用,只能使用DBMS_STATS来收集信息。
统计信息收集如下数据:
(1)表自身的分析: 包括表中的行数,数据块数,行长等信息。
(2)列的分析:包括列值的重复数,列上的空值,数据在列上的分布情况。
(3)索引的分析: 包括索引叶块的数量,索引的深度,索引的聚合因子等。
这些统计信息存放在数据字典里,如:
DBA_TABLES
DBA_OBJECT_TABLES
DBA_TAB_STATISTICS
DBA_TAB_COL_STATISTICS
DBA_TAB_HISTOGRAMS
DBA_INDEXES
DBA_IND_STATISTICS
DBA_CLUSTERS
DBA_TAB_PARTITIONS
DBA_TAB_SUBPARTITIONS
DBA_IND_PARTITIONS
DBA_IND_SUBPARTITIONS
DBA_PART_COL_STATISTICS
DBA_PART_HISTOGRAMS
DBA_SUBPART_COL_STATISTICS
DBA_SUBPART_HISTOGRAMS
包含表行数,使用的块数,空的块数,块的使用率,行迁移和链接的数量,pctfree,pctused的数据,行的平均大小:
SELECT NUM_ROWS, --表中的记录数
BLOCKS, --表中数据所占的数据块数
EMPTY_BLOCKS, --表中的空块数
AVG_SPACE, --数据块中平均的使用空间
CHAIN_CNT, --表中行连接和行迁移的数量
AVG_ROW_LEN --每条记录的平均长度
FROM USER_TABLES
包含索引的深度(B-Tree的级别),索引叶级的块数量,集群因子(clustering_factor), 唯一值的个数。
SELECT BLEVEL, --索引的层数
LEAF_BLOCKS, --叶子结点的个数
DISTINCT_KEYS, --唯一值的个数
AVG_LEAF_BLOCKS_PER_KEY, --每个KEY的平均叶块个数
AVG_DATA_BLOCKS_PER_KEY, --每个KEY的平均数据块个数
CLUSTERING_FACTOR --群集因子
FROM USER_INDEXES
包含 唯一的值个数,列最大小值,密度(选择率),数据分布(直方图信息),NUll值个数
SELECT
NUM_DISTINCT, --唯一值的个数
LOW_VALUE, --列上的最小值
HIGH_VALUE, --列上的最大值
DENSITY, --选择率因子(密度)
NUM_NULLS, --空值的个数
NUM_BUCKETS, --直方图的BUCKET个数
HISTOGRAM --直方图的类型
FROM USER_TAB_COLUMNS
判断统计信息是否准确,比较一下表记录的count 和 num_rows 值就知道了。 如果是一致的,就说明是最新的,如果不一致,可能就需要分析了。
注:本文主要摘自 以下几篇文章及网络整理
Oracle Statistic 统计信息 小结
https://blog.csdn.net/tianlesoftware/article/details/4668723
Oracle 判断 并 手动收集 统计信息 脚本
https://blog.csdn.net/tianlesoftware/article/details/6445868
Oracle分析及动态采样
https://www.cndba.cn/Dave/article/1575