ITPub博客

首页 > 数据库 > Oracle > oracle 10g表没有收集统计信息的一点研究与思考

oracle 10g表没有收集统计信息的一点研究与思考

原创 Oracle 作者:wisdomone1 时间:2014-08-27 19:25:03 1 删除 编辑
个人简介:
   8年oracle从业经验,具备丰富的oracle技能,目前在国内北京某专业oracle服务公司从事高级技术顾问。
   
   服务过的客户:
          中国电信
          中国移动
          中国联通
          中国电通
          国家电网
          四川达州商业银行
          湖南老百姓大药房
          山西省公安厅
          中国邮政
          北京302医院     
          河北廊坊新奥集团公司
  
 项目经验:
           中国电信3G项目AAA系统数据库部署及优化
           中国联通CRM数据库性能优化
           中国移动10086电商平台数据库部署及优化
           湖南老百姓大药房ERR数据库sql优化项目
           四川达州商业银行TCBS核心业务系统数据库模型设计和RAC部署及优化
           四川达州商业银行TCBS核心业务系统后端批处理存储过程功能模块编写及优化
           北京高铁信号监控系统RAC数据库部署及优化
           河南宇通客车数据库性能优化
           中国电信电商平台核心采购模块表模型设计及优化
           中国邮政储蓄系统数据库性能优化及sql优化
           北京302医院数据库迁移实施
           河北廊坊新奥data guard部署及优化
           山西公安厅身份证审计数据库系统故障评估
           
  
 联系方式:
          手机:18201115468
           qq   :   305076427
           qq微博: wisdomone1
           
           新浪微博:wisdomone9
          
           qq群:275813900    
          
           itpub博客名称:wisdomone1    http://blog.itpub.net/9240380/


数据库环境:
    oracle 10.2.0.5
操作系统环境
   aix 6.1

问题概述:
    某客户sql相关表的统计信息

Owner Table Name Tablespace Table Type Status Rows Blocks Avg Row Len Chain Count Degree Cache Analyzed
CUTM BD_BANK_TYPE DATA_TM Normal Table VALID 19 4 98 0 1 N 2013-08-03 02:52:00
CUTM RM_PARTY_RELATION DATA_TM Normal Table VALID 362104 9440 174 0 1 N 2014-08-01 11:02:44
CUTM TMP_FTS_ACC_REL Normal Table VALID 1 N
CUTM TMP_ACCNT_BALANCE Normal Table VALID 1 N
CUTM TMP_FTS_ACC_REL_ALL Normal Table VALID 1 N
CURAH RA_AD_OFFICE_PAY DATA_RAH_EXT01 Normal Table VALID 22172056 1242224 410 0 1 N 2014-07-29 12:23:31
CURAH RA_BASE_BUSINESS_OFFICE_INFO DATA_RA Normal Table VALID 177725 6304 254 0 1 N 2014-06-17 11:03:39
      由上可知,tmp前缀的表统计信息全部为空,这是何因呢?
上述表统计信息获取自dba_tables数据字典(注:关于dba_tables详见oracle官方手册)

问题分析:
      表上没有统计信息可能因素如下:
     
                  (注:陈旧即表上的数据DML变更没有超过10%,此特性通过dba_tab_modifications字典实现,具体请参考官方手册)


      如下为自动收集统计信息相关的一些官方注解 

Gather_stats_job

1,oracle通过gather_stats_job自动收集统计信息

2,统计信息陈旧
由调度器在固定的时间窗口运行及管理
4,这个固定的时间窗口即:从晚上10点至次日早上6点,周末是全天不限时
属性控制,gather_stats_job在时间窗口关闭是否继续运行默认情况下,stop_on_window_close属性值为true,即时间窗口关闭会中断gather_stats_job执行,余下的统计信息
收集会在下一次时间窗口继续执行
其实是通过调用dbms_stats.gather_database_stats_job_proc收集统计信息
只会收集统计信息陈旧或统计信息不存在的对象,且底层对象要变更大于10%以上的记录
是内部过程,它的功能类似于使用选项gather auto 选项的dbms_stats.gather_database_stats。

综上所述,tmp前缀的表可能正好执行DML处在自动收集统计任务之外的时间窗口,所以没有收集统计信息

问题测试:

获取自动收集统计信息JOB的信息
  1*  select log_id,log_date,owner,job_name,status,req_start_date,actual_start_date,run_duration,cpu_used from dba_scheduler_job_run_details where lower(job_name) like '%gather_stats_job%' order by log_id

LOG_ID LOG_DATE                            OWNER JOB_NAME                  STATUS     ACTUAL_START_DATE              RUN_DURATION              CPU_USED
------ ----------------------------------- ----- ------------------------- ---------- ------------------------------ ------------------------- -------------------------
   436 2014-07-31 22:55:17.477559          SYS   GATHER_STATS_JOB          SUCCEEDED  2014-07-31 22:54:53.775509     +000 00:00:24             +000 00:00:05.31
   451 2014-08-02 09:33:01.565868          SYS   GATHER_STATS_JOB          SUCCEEDED  2014-08-02 09:32:15.493005     +000 00:00:46             +000 00:00:11.20
   493 2014-08-05 02:46:53.737519          SYS   GATHER_STATS_JOB          SUCCEEDED  2014-08-05 02:46:18.885318     +000 00:00:35             +000 00:00:07.18
   515 2014-08-07 04:28:10.499073          SYS   GATHER_STATS_JOB          SUCCEEDED  2014-08-07 04:27:39.841948     +000 00:00:31             +000 00:00:08.03
   534 2014-08-09 10:30:31.265575          SYS   GATHER_STATS_JOB          SUCCEEDED  2014-08-09 10:29:27.029581     +000 00:01:04             +000 00:00:07.35
   570 2014-08-11 22:20:18.896573          SYS   GATHER_STATS_JOB          SUCCEEDED  2014-08-11 22:19:29.831058     +000 00:00:49             +000 00:00:08.36
   602 2014-08-12 23:18:26.573014          SYS   GATHER_STATS_JOB          SUCCEEDED  2014-08-12 23:17:49.866337     +000 00:00:37             +000 00:00:08.41
   633 2014-08-13 22:17:33.236311          SYS   GATHER_STATS_JOB          SUCCEEDED  2014-08-13 22:16:49.790815     +000 00:00:43             +000 00:00:09.04
   657 2014-08-15 02:32:11.726540          SYS   GATHER_STATS_JOB          SUCCEEDED  2014-08-15 02:31:35.913797     +000 00:00:36             +000 00:00:10.00
   685 2014-08-19 02:49:17.709531          SYS   GATHER_STATS_JOB          SUCCEEDED  2014-08-19 02:46:58.042017     +000 00:02:20             +000 00:00:37.87
   715 2014-08-21 01:48:24.366631          SYS   GATHER_STATS_JOB          SUCCEEDED  2014-08-21 01:45:49.330810     +000 00:02:35             +000 00:00:19.26

LOG_ID LOG_DATE                            OWNER JOB_NAME                  STATUS     ACTUAL_START_DATE              RUN_DURATION              CPU_USED
------ ----------------------------------- ----- ------------------------- ---------- ------------------------------ ------------------------- -------------------------
   725 2014-08-24 23:27:47.297346          SYS   GATHER_STATS_JOB          SUCCEEDED  2014-08-24 23:27:21.684993     +000 00:00:26             +000 00:00:05.28

12 rows selected.



dbms_scheduler.run_job官方注解

RUN_JOB Procedure

This procedure runs a job immediately.

Syntax

DBMS_SCHEDULER.RUN_JOB (
   job_name                IN VARCHAR2,
   use_current_session     IN BOOLEAN DEFAULT TRUE);


Parameters

Table 93-48 RUN_JOB Procedure Parameters

Parameter Description

job_name

The name of the job being run

use_current_session

This specifies whether the job run should occur in the same session as the one that the procedure was invoked from.

When use_current_session is set to TRUE:

  • You can test a job and see any possible errors on the command line.

  • run_count, last_start_date, last_run_duration, and failure_count are not updated.

  • RUN_JOB can be run in parallel with a regularly scheduled job run.

When use_current_session is set to FALSE:

  • You need to check the job log to find error information.

  • run_count, last_start_date, last_run_duration, and failure_count are updated.

  • RUN_JOB fails if a regularly scheduled job is running.


Usage Notes

The job does not have to be enabled. If the job is disabled, the following validity checks are performed before running it:

  • The job points to a valid job class.The job owner has EXECUTE privileges on the job class.If a program or chain is referenced, the program/chain exists.If a program or chain is referenced, the job owner has privileges to execute the program/chain.All argument values have been set (or have defaults).The job owner has the CREATE EXTERNAL JOB privilege if this is an external job.

The job can be run in two different modes. One is in the current user session. In this case, the call to RUN_JOB will block until it has completed the job. Any errors that occur during the execution of the job will be returned as errors to the RUN_JOB procedure. The other option is to run the job immediately like a regular job. In this case, RUN_JOB returns immediately and the job will be picked up by the coordinator and passed on to a job slave for execution. The Scheduler views and logs must be queried for the outcome of the job.

Multiple user sessions can use RUN_JOB in their sessions simultaneously when use_current_session is set to TRUE.

When using RUN_JOB with jobs that point to chains, use_current_session must be FALSE.

RUN_JOB requires that you be the owner of the job or have ALTER privileges on that job. You can also run a job if you have the CREATE ANY JOB privilege. 


手工调用自动收集统计信息job
SQL> exec dbms_scheduler.run_job('GATHER_STATS_JOB',true);

PL/SQL procedure successfully completed.

再次查看自动收集统计信息信息
SQL> col job_name for a25
SQL> alter session set nls_timestamp_tz_format='yyyy-mm-dd hh24:mi:ss.ff';

Session altered.

SQL>  select log_id,log_date,owner,job_name,status,req_start_date,actual_start_date,run_duration,cpu_used from dba_scheduler_job_run_details where lower(job_name) like '%gather_stats_job%' order by log_id;

LOG_ID LOG_DATE                            OWNER JOB_NAME                  STATUS     ACTUAL_START_DATE              RUN_DURATION              CPU_USED
------ ----------------------------------- ----- ------------------------- ---------- ------------------------------ ------------------------- -------------------------
   436 2014-07-31 22:55:17.477559          SYS   GATHER_STATS_JOB          SUCCEEDED  2014-07-31 22:54:53.775509     +000 00:00:24             +000 00:00:05.31
   451 2014-08-02 09:33:01.565868          SYS   GATHER_STATS_JOB          SUCCEEDED  2014-08-02 09:32:15.493005     +000 00:00:46             +000 00:00:11.20
   493 2014-08-05 02:46:53.737519          SYS   GATHER_STATS_JOB          SUCCEEDED  2014-08-05 02:46:18.885318     +000 00:00:35             +000 00:00:07.18
   515 2014-08-07 04:28:10.499073          SYS   GATHER_STATS_JOB          SUCCEEDED  2014-08-07 04:27:39.841948     +000 00:00:31             +000 00:00:08.03
   534 2014-08-09 10:30:31.265575          SYS   GATHER_STATS_JOB          SUCCEEDED  2014-08-09 10:29:27.029581     +000 00:01:04             +000 00:00:07.35
   570 2014-08-11 22:20:18.896573          SYS   GATHER_STATS_JOB          SUCCEEDED  2014-08-11 22:19:29.831058     +000 00:00:49             +000 00:00:08.36
   602 2014-08-12 23:18:26.573014          SYS   GATHER_STATS_JOB          SUCCEEDED  2014-08-12 23:17:49.866337     +000 00:00:37             +000 00:00:08.41
   633 2014-08-13 22:17:33.236311          SYS   GATHER_STATS_JOB          SUCCEEDED  2014-08-13 22:16:49.790815     +000 00:00:43             +000 00:00:09.04
   657 2014-08-15 02:32:11.726540          SYS   GATHER_STATS_JOB          SUCCEEDED  2014-08-15 02:31:35.913797     +000 00:00:36             +000 00:00:10.00
   685 2014-08-19 02:49:17.709531          SYS   GATHER_STATS_JOB          SUCCEEDED  2014-08-19 02:46:58.042017     +000 00:02:20             +000 00:00:37.87
   715 2014-08-21 01:48:24.366631          SYS   GATHER_STATS_JOB          SUCCEEDED  2014-08-21 01:45:49.330810     +000 00:02:35             +000 00:00:19.26

LOG_ID LOG_DATE                            OWNER JOB_NAME                  STATUS     ACTUAL_START_DATE              RUN_DURATION              CPU_USED
------ ----------------------------------- ----- ------------------------- ---------- ------------------------------ ------------------------- -------------------------
   725 2014-08-24 23:27:47.297346          SYS   GATHER_STATS_JOB          SUCCEEDED  2014-08-24 23:27:21.684993     +000 00:00:26             +000 00:00:05.28
   751 2014-08-27 04:58:12.980583          SYS   GATHER_STATS_JOB          SUCCEEDED  2014-08-27 04:57:51.591701     +000 00:00:21             +000 00:00:08.08

13 rows selected.




创建测试表
[ora10g@seconary ~]$ sqlplus tbs_11204/system

SQL*Plus: Release 10.2.0.5.0 - Production on Wed Aug 27 05:15:55 2014

Copyright (c) 1982, 2010, Oracle.  All Rights Reserved.


Connected to:
Oracle Database 10g Enterprise Edition Release 10.2.0.5.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options


SQL> create table t_stat(a int,b int);

Table created.

插入数据
SQL> insert into t_stat select level,level from dual connect by level<=1000;

1000 rows created.

SQL> commit;

Commit complete.

确认没有收集统计信息
SQL> set linesize 300
SQL> select table_name,num_rows,blocks,degree,avg_row_len,chain_cnt from user_tables where lower(table_name)='t_stat';

TABLE_NAME                       NUM_ROWS     BLOCKS DEGREE     AVG_ROW_LEN  CHAIN_CNT
------------------------------ ---------- ---------- ---------- ----------- ----------
T_STAT                                                        1

SQL> 

手工执行上述自动收集统计信息的job
SQL> exec dbms_scheduler.run_job('GATHER_STATS_JOB',true);

PL/SQL procedure successfully completed.

再次查看上述表已收集了统计信息
SQL> select table_name,num_rows,blocks,degree,avg_row_len,chain_cnt from user_tables where lower(table_name)='t_stat';

TABLE_NAME                       NUM_ROWS     BLOCKS DEGREE     AVG_ROW_LEN  CHAIN_CNT
------------------------------ ---------- ---------- ---------- ----------- ----------
T_STAT                               1000          5          1           7          0


       再回到开始的问题,从上述测试来看,可能原因就是tmp前缀的表刚好在自动统计信息收集完时间窗口外进行DML操作,所以没有统计信息


       进一步分析,如果作过数据库开发的人,可能会猜测这种TMP前缀的表多用于存储过程之中,用于临时存储存储过程中间加工的结果,
可以通过如下脚本获取tmp前缀的表是否位于存储过程中
select owner,name from dba_source where lower(text) like '%tmp前缀的表%';


      如果位于存储过程,存储过程是否定时运行呢,如果是这样的话,有种可能就是每次执行时tmp前缀的表在存储过程执行完,就drop table了
所以即使第2次自动收集统计信息运行,当然不会收集不存在的对象了


       tmp前缀的表是否频繁创建删除,可通过如下脚本定位与诊断,主要看created,last_ddl_time列的时间,是不是很新的时间,
大家知道自动统计信息每天都会执行,所以可以在下述脚本在2天重复运行,对比下,就知道tmp前缀的表是否频繁drop然后重建了
select owner,object_name,object_id,object_type,created,last_ddl_time,timestamp,status
from dba_objects  
where object_name='tmp前缀的表' ;


       当然,这一切仍旧去客户现场进行证实

       没有收集表的统计信息,SQL在生成执行计划时,该如何处理呢,我们知道,如果表上没有统计信息时,oracle会采用动态采样技术去获取表
及列和索引的统计信息,大家注意标红部分,表明采用了动态采样技术
Id Operation Name Rows Bytes Cost (%CPU) Time
0 SELECT STATEMENT       23 (100)  
1    TABLE ACCESS FULL BD_BANK_TYPE 1 17 3 (0) 00:00:01
2    SORT AGGREGATE   1 87    
3      FILTER          
4        TABLE ACCESS BY INDEX ROWID TMP_ACCNT_BALANCE 115 10005 5 (0) 00:00:01
5          INDEX RANGE SCAN IDX_TMP_BALANCE_DATE 53   1 (0) 00:00:01
6        VIEW index$_join$_004 1 104 3 (34) 00:00:01
7          HASH JOIN          
8            INDEX RANGE SCAN IDX_PROC_ACC_NUM_L1 1 104 1 (0) 00:00:01
9            INDEX RANGE SCAN IDX_PROC_ACC_NUM_L2 1 104 1 (0) 00:00:01
10    SORT AGGREGATE   1 74    
11      FILTER          
12        TABLE ACCESS BY INDEX ROWID TMP_ACCNT_BALANCE 132 9768 5 (0) 00:00:01
13          INDEX RANGE SCAN IDX_TMP_BALANCE_DATE 53   1 (0) 00:00:01
14        VIEW index$_join$_006 1 104 3 (34) 00:00:01
15          HASH JOIN          
16            INDEX RANGE SCAN IDX_PROC_ACC_NUM_L1 1 104 1 (0) 00:00:01
17            INDEX RANGE SCAN IDX_PROC_ACC_NUM_L3 1 104 1 (0) 00:00:01
18    SORT AGGREGATE   1 63    
19      FILTER          
20        HASH JOIN   997 62811 5441 (1) 00:01:06
21          TABLE ACCESS BY INDEX ROWID RA_AD_OFFICE_PAY 998 50898 5256 (1) 00:01:04
22            INDEX RANGE SCAN INDEX_BUSINESS_DATE 28890   104 (0) 00:00:02
23          INDEX FAST FULL SCAN PK_RBBOI 177K 2082K 184 (1) 00:00:03
24        VIEW index$_join$_009 1 104 3 (34) 00:00:01
25          HASH JOIN          
26            INDEX RANGE SCAN IDX_PROC_ACC_NUM_L1 1 104 1 (0) 00:00:01
27            INDEX RANGE SCAN IDX_PROC_ACC_NUM_L4 1 104 1 (0) 00:00:01
28    VIEW   1 335 23 (18) 00:00:01
29      SORT ORDER BY   1 234 23 (18) 00:00:01
30        FILTER          
31          NESTED LOOPS   3 702 22 (14) 00:00:01
32            VIEW VW_NSO_2 23 299 19 (11) 00:00:01
33              HASH UNIQUE   1 897    
34                NESTED LOOPS   23 897 19 (11) 00:00:01
35                  VIEW VW_NSO_1 2 26 11 (19) 00:00:01
36                    SORT UNIQUE   2 44 11 (82) 00:00:01
37                      UNION-ALL          
38                        FAST DUAL   1   2 (0) 00:00:01
39                        TABLE ACCESS BY INDEX ROWID RM_PARTY_RELATION 1 44 7 (0) 00:00:01
40                          INDEX RANGE SCAN IDX_PARTYREL_PARENTID_VIEW 7   3 (0) 00:00:01
41                  TABLE ACCESS BY INDEX ROWID TMP_FTS_ACC_REL 11 286 4 (0) 00:00:01
42                    INDEX RANGE SCAN IDX_PROC_MAIN_CORP_ID 11   1 (0) 00:00:01
43            TABLE ACCESS BY INDEX ROWID TMP_ACCNT_BALANCE 3 663 2 (0) 00:00:01
44              INDEX RANGE SCAN IDX_TMP_PK_ACTUAL_ACCNT 3   1 (0) 00:00:01
45          FILTER          
46            HASH GROUP BY   1 44 2078 (1) 00:00:25
47              TABLE ACCESS FULL RM_PARTY_RELATION 1761 77484 2077 (1) 00:00:25

  .dynamic sampling used for this statement

摘录动态采样官方注解

何时使用动态采样

1,  对于查询速度很快的SQL,比如仅运行不到几秒,使用动态采样大可不必,反而增大成本,影响性能,

2,  但是,在如下情形下,采用动态采样是有益的:

?  使用动态采样可以选取更好的执行计划

?  动态采样的时间仅占更个SQL查询的很小一部分

?  SQL查询会执行很多次

3, 动态采样既适用于单表谓词一部分,也适用于未采用动态采样的常规统计信息评估

如何使用动态采样提升数据库性能

  通过参数optimizer_dynamic_samplingr控制动态采样的级别,值为0-10,默认为2

?  0表示禁用动态采样

?  值越大,动态采样越明显或者越强大,这个是基于采样表是否收集过统计信息以及采样消耗的IO资源

@  如果采样表没有数据插入,删除或修改,动态采样依旧可以重复执行

@  如果配置optimizer_features_enable=小于9.2.0,则禁用动态采样功能

动态采样的采样级别

l   动态采样级别可以通过参数optimizer_dynamic_sampling或SQL HINT进行配置

l   optimizer_dynamic_sampling配置值如下:

?  level 0:禁用动态采样

?  level 1:如果满足如下条件,则采样所有没有被收集统计信息的表

           SQL到少有1个表未采分析

           未分析的表与另一个表进行关联或者出现在子查询

           或不能   合并的视图

           没有索引的未被分析的表

           未被分析的表要比动态采样分析的表更多的数据块。默认       情况下,采样的数据块个数为32个数据块

level 2:对所有未分析的表进行动态采样,采样数据块个数是默认采样数据块个数的2倍

level 3:在level 2的基础上,加上用于评估潜在的动态采样谓词的所有表。动态采样数据块个数,默认采取动态采样默认的数据块个数,对于未被分析过的表,采样数据块个数是默认的2倍

level 4:在level 3的基础上,加上单表谓词有2个的所有表。其它同上

level 5,6,7,8,9:适应用于之前所有的level,分别以上述采样级别的2,4,8,32,128倍数进行动态采样数据块

level 10:满足level 9,且扫描使用表上所有的数据块,应用动态采样


动态采样在
sql hint中应用,即dynamic_sampling:

                     level 0:同上

                     level 1:默认采样32个数块

                     level 2,3,4,5,6,7,8,9,同上,是默认动态采样数据块的2,4,8,16,32,64,128,256倍

                     level 10:动态采样会读取表所有的数据块


测试结论:
              1,如果表DML进行DML或大批量数据加载,且处于自动收集统计信息job之外,是不会收集表相关统计信息
              
              2,如果存储过程调用表,且表在存储过程中状态周期为创建,使用,删除,依旧不会收集表相关统计信息
                  (注:此存储过程运行时间要短,不能与自动收集统计信息重合)
              
              3,通过dba_objects获取表是否频繁创建,存活周期很短
             
              4,如果表没有收集统计信息,数据库会采用动态采样技术请及时收集表相关的统计信息

              5,如果表没有收集统计信息,也可以通过如下脚本对比诊断,表统计信息收集的历史过程,此字典一般存储31天的数据,如果依旧没有信息,
                 原因可能是上述的几个原因之一
                       select operation,target,start_time,end_time 
                       from dba_optstat_operations 
                       where lower(target) like '%tmp前缀的表%';

             6, 也可能是与相关的SQL刚首次运行,正好未在自动收集统计信息JOB之内运行,可以通过dba_hist_sql_stats进行诊断与确认 


参考资料:
           oracle 10g在线官方文档:
http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_stats.htm#CIHBIEII

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/9240380/viewspace-1259114/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
提供针对oracle初学者及进阶的数据库培训,欢迎大家咨询: 微信: wisdomone 微信公众号: lovedb 工作经历: 中国普天 北京科蓝 北京云和恩墨 北京神州新桥

注册时间:2008-04-04

  • 博文量
    2189
  • 访问量
    12689897