oracle 10g表沒有收集統計資訊的一點研究與思考

wisdomone1發表於2014-08-27
個人簡介:
   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_samplingSQL 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,同上,是預設動態取樣資料塊的248163264128256

                     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部落格 ” ,連結:http://blog.itpub.net/9240380/viewspace-1259114/,如需轉載,請註明出處,否則將追究法律責任。

相關文章