在Oracle 11g partiitioned表上收集并发统计信息

Fra*_*sco 14 oracle etl data-warehouse table-statistics oracle11g

我正在开发Oracle 11g上的DWH.我们有一些大表(2.5亿行),按值划分.每个分区都分配给不同的馈送源,每个分区独立于其他分区,因此可以同时加载和处理它们.

数据分布非常不均匀,我们有数百万行的分区,并且分区不超过一百行,但我没有选择分区方案,顺便说一下我无法改变它.

考虑到数据量,我们必须确保每个分区始终具有最新的统计信息,因为如果后续的详细说明没有对数据的最佳访问权限,它们将永远持续下去.

所以对于每个并发的ETL线程,我们

  1. 截断分区
  2. 从暂存区域加载数据

SELECT /*+ APPEND */ INTO big_table PARTITION(part1) FROM temp_table WHERE partition_colum = PART1

(这种方式我们有直接路径,我们不锁定整个表)

  1. 我们收集修改分区的统计信息.

在项目的第一阶段,我们使用了这个APPROX_GLOBAL_AND_PARTITION策略并且像魅力一样工作

 dbms_stats.gather_table_stats(ownname=>myschema,
                              tabname=>big_table,
                              partname=>part1,
                              estimate_percent=>1,
                              granularity=>'APPROX_GLOBAL_AND_PARTITION',
                              CASCADE=>dbms_stats.auto_cascade,
                              degree=>dbms_stats.auto_degree) 
Run Code Online (Sandbox Code Playgroud)

但是,我们有一个缺点,当我们加载一个小分区时,APPROX_GLOBAL部分占主导地位(仍然比GLOBAL快很多),对于一个小分区,我们有例如10秒的加载和20分钟的统计.

所以我们建议切换到11g 的INCREMENTAL STATS功能,这意味着你没有指定你修改过的分区,你把所有参数保留在auto中,而Oracle做了它的魔力,自动了解哪个分区有被感动了.它确实有效,我们已经加快了小分区的速度.打开电话后,电话就变成了

 dbms_stats.gather_table_stats(ownname=>myschema,
                              tabname=>big_table,
                              estimate_percent=>dbms_stats.auto_sample_size,
                              granularity=>'AUTO',
                              CASCADE=>dbms_stats.auto_cascade,
                              degree=>dbms_stats.auto_degree) 
Run Code Online (Sandbox Code Playgroud)

请注意,您不再通过分区,并且未指定样本百分比.

但是,我们有一个缺点,可能比前一个更糟糕,这与我们的高水平并行性有关.

假设我们有两个同时启动的大分区,它们几乎同时也会完成加载阶段.

  1. 第一个线程结束插入语句,提交并启动统计信息收集.统计程序通知有2个分区已修改(这是正确的,一个已满,第二个被截断,正在进行事务),正确更新了两个分区的统计信息.

  2. 最终第二个分区结束,收集统计信息,它看到所有分区已经更新,并且什么也不做(这是不正确的,因为第二个线程同时提交了数据).

结果是:

PARTITION NAME | LAST ANALYZED        | NUM ROWS | BLOCKS | SAMPLE SIZE
-----------------------------------------------------------------------
PART1          | 04-MAR-2015 15:40:42 | 805731   | 20314  | 805731
PART2          | 04-MAR-2015 15:41:48 | 0        | 16234  | (null)
Run Code Online (Sandbox Code Playgroud)

结果是我偶尔会出现非最佳计划(这意味着杀死会话,手动刷新统计数据,再次手动启动进动).

我甚至试图在收集上放置一个独占锁,所以一次只能有一个线程可以在同一个表上收集统计信息,但没有任何改变.

恕我直言,这是一个奇怪的行为,因为统计程序,第二次被调用,应该检查第二个分区上的最后一次提交,并且应该看到它比最后的统计数据收集时间更新.但似乎没有发生.

难道我做错了什么?这是Oracle的错误吗?如何保证所有统计信息始终是最新的,并且启用了增量统计功能,并且具有高级别的并发性?

Fra*_*sco 2

我设法与这个功能达成了一个不错的妥协。

PROCEDURE gather_tb_partiz(
    p_tblname IN VARCHAR2,
    p_partname IN VARCHAR2)
IS
  v_stale all_tab_statistics.stale_stats%TYPE;
BEGIN
  BEGIN
    SELECT stale_stats
    INTO v_stale
    FROM user_tab_statistics
    WHERE table_name = p_tblname
    AND object_type = 'TABLE';
  EXCEPTION
  WHEN NO_DATA_FOUND THEN
    v_stale := 'YES';
  END;
  IF v_stale = 'YES' THEN
    dbms_stats.gather_table_stats(ownname=>myschema, 
                                  tabname=> p_tblname,
                                  partname=>p_partname,
                                  degree=>dbms_stats.auto_degree,
                                  granularity=>'APPROX_GLOBAL AND PARTITION') ;
  ELSE
    dbms_stats.gather_table_stats(ownname=>myschema,
                                 tabname=>p_tblname,
                                 partname=>p_partname,
                                 degree=>dbms_stats.auto_degree,
                                 granularity=>'PARTITION') ;
  END IF;
END gather_tb_partiz;
Run Code Online (Sandbox Code Playgroud)

在每个 ETL 结束时,如果添加/删除/修改的行数足够低,不会将表标记为过时(默认情况下为 10%,可以使用 STALE_PERCENT 参数进行调整),我仅收集分区统计信息;否则我会收集全局和分区统计信息。

这使得小分区的 ETL 快速,因为不需要重新收集全局分区,并且大分区安全,因为任何后续查询都将具有新的统计信息,并且可能会使用最佳计划。

无论如何,增量统计信息都是启用的,因此每当需要重新计算全局时,它都非常快,因为聚合分区级别统计信息并且不执行完整扫描。

我不确定启用增量后,“APPROX_GLOBAL AND PARTITION”和“GLOBAL AND PARTITION”是否有所不同,因为增量和近似都基本上执行相同的操作:聚合统计数据和直方图,而不进行完整扫描。