2017-01-01 102 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重新收集索引统计信息。

不要外包统计收集到其他计划的工作。统计数据非常重要且棘手,应该与任何正在进行重大数据更改的程序完全集成。