2017-01-01 43 views
1

我有表ABC每天的時間間隔分區。每個分區都用於特定日期的查詢。即使我每天安排job @nyt來收集統計信息,那麼在統計信息收集之前使用該表的查詢將不會使用最佳計劃。收集分區表上的統計信息

+0

添加查詢計劃和ddl –

+0

每當添加新分區並且存在大量數據加載時,我想知道我們是否具有自動收集的統計信息。如果沒有,那麼我們如何收集統計信息? – oracle

+0

看看包DBMS_STATS.GATHER_TABLE_STATISTICS,在那裏你也可以指定分區。 –

回答

0

收集分區表上的優化程序統計信息並不是一件簡單的任務,但有一些注意事項。 特別是在日常分區模式下,它可能不是每天收集一次分區統計數據的最佳解決方案。

爲了演示它,假設我們沒有每日模式,但每年的事務數據分區。問題是, 可以在1月1日(或1月1日或12月31日)收集統計數據嗎? 答案是肯定沒有,因爲在第一種情況下分區將被認爲(幾乎)爲空,在後面的情況下 統計將是現實的,但是它們被收集得太遲。

考慮到這一點有IMO三種到處理它

1)不要聚集在所有的統計數據(並使用動態採樣)

2)反覆收集分區統計可能的方法(比如每個小時)

3)不收集統計數據,但將它們設置是這樣,查詢進行精細

最好的選擇取決於您的數據和訪問模式,所以我只考慮一些細節,那些選擇實施離子。

樣本數據

允許生成一個完整的和一個幾乎空日常分區的表。

該表在GROUP_ID列中有一個本地索引。練習的目的是在訪問小分區時獲取FULL TABLE SCAN ,並在訪問大分區時獲取INDEX ACCESS

CREATE TABLE mytab 
    ( id number not null, 
     group_id number, 
     trans_date date, 
     pad varchar2(4000)) 
PARTITION BY RANGE (trans_date) 
INTERVAL (NUMTODSINTERVAL(1,'DAY')) 
(
    PARTITION part_01 values LESS THAN (TO_DATE('31-12-2016','DD-MM-YYYY')) 
); 

create index mytab_idx1 on mytab(id) local; 
create index mytab_idx2 on mytab(group_id) local; 

-- full day partition 
insert into mytab (id, group_id, trans_date, pad) 
select rownum id, trunc(rownum/1000) group_id, to_date('31122016','ddmmyyyy'), lpad('x',3000,'x') from dual 
connect by level <= 100000; 
commit; 

-- nearly empty day partition 
insert into mytab (id, group_id, trans_date, pad) 
select rownum id, trunc(rownum/1000) group_id, to_date('01012017','ddmmyyyy'), lpad('x',3000,'x') from dual 
connect by level <= 1000; 
commit; 

動態採樣

如果目標對象沒有統計可言,Oracle執行動態採樣(又名dynamic statistics) 有了一個小的開銷甲骨文計算統計數據,而解析的語句。所以它不能陳舊。

訪問幾乎是空的分區甲骨文適當選擇FULL TABLE SCAN

EXPLAIN PLAN SET STATEMENT_ID = 'jara1' into plan_table FOR 
select * from mytab 
where trans_date = TO_DATE('01-01-2017','DD-MM-YYYY') and group_id = 0; 

SELECT * FROM table(DBMS_XPLAN.DISPLAY('plan_table', 'jara1','ALL')); 

Plan hash value: 4018216072 

------------------------------------------------------------------------------------------------ 
| Id | Operation    | Name | Rows | Bytes | Cost (%CPU)| Time  | Pstart| Pstop | 
------------------------------------------------------------------------------------------------ 
| 0 | SELECT STATEMENT  |  | 958 | 1905K| 274 (0)| 00:00:01 |  |  | 
| 1 | PARTITION RANGE SINGLE|  | 958 | 1905K| 274 (0)| 00:00:01 |  3 |  3 | 
|* 2 | TABLE ACCESS FULL | MYTAB | 958 | 1905K| 274 (0)| 00:00:01 |  3 |  3 | 
------------------------------------------------------------------------------------------------ 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    2 - filter("TRANS_DATE"=TO_DATE(' 2017-01-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss') 
       AND "GROUP_ID"=0) 

Note 
----- 
    - dynamic statistics used: dynamic sampling (level=2) 

...在訪問完整的分區INDEX ACCESS用於

EXPLAIN PLAN SET STATEMENT_ID = 'jara1' into plan_table FOR 
select * from mytab 
where trans_date = TO_DATE('31-12-2016','DD-MM-YYYY') and group_id = 0; 

SELECT * FROM table(DBMS_XPLAN.DISPLAY('plan_table', 'jara1','ALL')); 

Plan hash value: 984912596 

------------------------------------------------------------------------------------------------------------------------- 
| Id | Operation         | Name  | Rows | Bytes | Cost (%CPU)| Time  | Pstart| Pstop | 
------------------------------------------------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT       |   | 1608 | 3198K| 9021 (1)| 00:00:01 |  |  | 
| 1 | PARTITION RANGE SINGLE     |   | 1608 | 3198K| 9021 (1)| 00:00:01 |  2 |  2 | 
|* 2 | TABLE ACCESS BY LOCAL INDEX ROWID BATCHED| MYTAB  | 1608 | 3198K| 9021 (1)| 00:00:01 |  2 |  2 | 
|* 3 | INDEX RANGE SCAN      | MYTAB_IDX2 | 1608 |  | 2880 (1)| 00:00:01 |  2 |  2 | 
------------------------------------------------------------------------------------------------------------------------- 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    2 - filter("TRANS_DATE"=TO_DATE(' 2016-12-31 00:00:00', 'syyyy-mm-dd hh24:mi:ss')) 
    3 - access("GROUP_ID"=0) 

Note 
----- 
    - dynamic statistics used: dynamic sampling (level=2) 

所以我們看到,動態採樣工作正常,選擇正確的訪問方法。

收集分區統計經常

重複採集工作減輕了問題,該分區在不斷增加。

期限取決於交易利率。

對一個分區僅

exec dbms_stats.gather_table_stats(OWNNAME=>user,TABNAME=>'MYTAB', PARTNAME=>'SYS_P10030', CASCADE=> TRUE); 

必須避免最壞的情況收集統計數據的例子是*統計指向該分區是空的,但(在此期間)的分區是重填充。

設置統計

這種方法假定,對於查詢「正確」的訪問路徑是已知的。在我們的示例 中,我們可以訪問幾乎爲空的分區,其中FULL TABLE SCAN,但對於此類分區 也可以使用索引訪問。因此,我們可以設置分區統計信息,以便始終進行INDEX ACCESS。

一個可能的(非常簡單的)模式是複製前一天的統計數據。

這個調用拷貝從分區SYS_P10029統計分區SYS_P10030

exec DBMS_STATS.COPY_TABLE_STATS (OWNNAME=>user,TABNAME=>'MYTAB',srcpartname=>'SYS_P10029',dstpartname=> 'SYS_P10030');  

所以換句話說,創立了統計開始爲充分填充分區的分區之後立即執行。

0

在我的應用程序中,我每天通過調度程序作業來運行此過程。它收集最近分區的統計信息。

PROCEDURE GatherIndexStats IS 

    CURSOR IndexPartition(indName IN VARCHAR2) IS 
    SELECT INDEX_NAME, PARTITION_NAME 
    FROM USER_IND_STATISTICS i 
     JOIN USER_TAB_PARTITIONS t USING (TABLE_NAME, PARTITION_NAME) 
    WHERE TABLE_NAME = 'ABC' 
     AND i.LAST_ANALYZED IS NULL 
     AND OBJECT_TYPE = 'PARTITION' 
     AND INDEX_NAME = indName 
    ORDER BY INDEX_NAME, PARTITION_NAME DESC 
    OFFSET 1 ROW FETCH FIRST 2 ROW ONLY; 

BEGIN 

    FOR aIndex IN (SELECT INDEX_NAME FROM USER_INDEXES WHERE TABLE_NAME = 'ABC') LOOP 
     FOR aInd IN IndexPartition(aIndex.INDEX_NAME) LOOP 
      DBMS_STATS.GATHER_INDEX_STATS(USER, aInd.INDEX_NAME, aInd.PARTITION_NAME); 
     END LOOP; 
    END LOOP; 

END GatherIndexStats; 

在我的應用程序需要得到唯一索引統計信息,而不是完整的表的統計信息。如果您想獲得索引和表格統計信息,請使用以下步驟:

PROCEDURE GatherTableStats IS 

    CURSOR TablePartition IS 
    SELECT INDEX_NAME, PARTITION_NAME 
    FROM USER_TAB_STATISTICS i 
     JOIN USER_TAB_PARTITIONS t USING (TABLE_NAME, PARTITION_NAME) 
    WHERE TABLE_NAME = 'ABC' 
     AND i.LAST_ANALYZED IS NULL 
     AND OBJECT_TYPE = 'PARTITION' 
    ORDER BY PARTITION_NAME DESC 
    OFFSET 1 ROW FETCH FIRST 2 ROW ONLY; 

BEGIN 

    FOR aPart IN TablePartition LOOP 
     DBMS_STATS.GATHER_TABLE_STATS(USER, 'ABC', aPart.PARTITION_NAME); 
    END LOOP; 

END GatherTableStats; 
0

統計信息應作爲任何過程的一部分收集,以顯着更改數據。不要依賴夜間工作來收集統計數據,特別是在大型數據倉庫中。

收集統計信息只在夜間的工作有許多潛在的缺點:

  1. 的處理有一個奇怪的時間依賴性。統計窗口可能會很難協調。有時如果工作太多,你關心的桌子可能沒有時間進行分析。
  2. 有幾種類型的統計作業(調度程序作業,DBA_JOBS,auto_tasks),所有這些類型都傾向於禁用更多的應用程序。
  3. 收集統計在錯誤的時間是差很多比根本沒有統計。如果沒有統計數據,那麼Oracle可以使用動態採樣來做一份體面的工作。但是,如果夜間工作恰好在表格爲空的短暫時間內收集統計數據,則統計數據可能會非常錯誤,並且性能將受到影響。我見過這種事多次發生;這些錯誤往往被歸咎於「環境差異」,但如果你離開關鍵的一步,那麼環境將隨機失敗。

作爲數據加載過程的一部分收集統計信息有許多潛在的優勢。既然你瞭解過程和表比一些普通的晚間統計工作,你可以採取的許多先進的功能優勢更好:

  1. 如果系統不是數據負載,然後並行可以像參數一起使用後忙度=> 8。
  2. 如果它是12c中的直接路徑寫入,則可以在使用GATHER_OPTIMIZER_STATISTICS提示加載數據時自動收集統計信息。
  3. 如果是間隔分區表,則可能需要設置增量統計信息收集。這使得該進程只花費時間收集分區的統計信息,並且全局統計信息將免費更新。
  4. 如果進程禁用並重建索引,則可以避免使用參數NOCASCADE => TRUE重新收集索引統計信息。

不要外包統計收集到其他計劃的工作。統計數據非常重要且棘手,應該與任何正在進行重大數據更改的程序完全集成。