减少查询的缓冲区高速缓存命中会导致随机性能问题

时间:2014-07-29 16:01:08

标签: sql performance oracle oracle11g

我们在生产中有一个UPDATE(下面),它每天处理的行数或多或少相同,但运行时间差异很大。有时几天查询会在2分钟内完成,而其他日子,查询可能需要20分钟。根据我对AWR数据的分析,罪魁祸首是I / O等待时间,每当查询速度减慢时,由于物理读取增加,缓存命中率下降。

查询的大纲如下:

update /*+ nologging parallel ( a 12 )  */ huge_table1 a  
set   col = 1
where  col1 > 'A'
  and    col2 < 'B'
  and    exists ( select /*+ parallel ( b 12 ) */ 1
                from   huge_table2 b
                where  b.col3 = a.col3 );

huge_table1和huge_table2包含大约1亿行,执行统计数据如下:

Day     EXECUTIONS ELAPSED_TIME_S_1EXEC CPU_TIME_S_1EXEC IOWAIT_S_1EXEC ROWS_PROCESSED_1EXEC BUFFER_GETS_1EXEC  DISK_READS_1EXEC DIRECT_WRITES_1EXEC
------- ----------- -------------------- ---------------- -------------- -------------------- ----------------- ----------------- -------------------
      1           1              133.055           69.110         23.325          2178085.000       3430367.000         90522.000           42561.000
      2           1              123.580           65.020         20.282          2179404.000       3341566.000         86614.000           38925.000
      3           1             1212.762           72.800       1105.084          1982658.000       3131695.000        268260.000           38446.000
      4           1             1085.773           59.600        996.642          1965309.000       2954480.000        200612.000           26790.000

如上所述,LIO在每种情况下几乎保持不变,尽管由于IO等待增加,在第3天和第4天经过的时间增加,如果我的假设是正确的,则由PIO的增加引起。根据Tom Kyte的说法,调整应该专注于减少LIO而不是PIO,并且随着LIO的减少,PIO也将减少。但在这种情况下,LIO始终保持不变,但PIO的变化很大。

我的问题 - 这里可以采用什么调整策略?

4 个答案:

答案 0 :(得分:1)

我会:

- &GT;检查两种情况的执行计划。 - &GT;检查IO子系统运行状况 - &GT;监视服务器运行的时间,并确保IO sybsystem没有被另一个进程饱和。

此外,什么样的I / O引导读取事件?顺序,并行,分散?...在​​这里,您可以了解计划遵循的策略以执行更新......

是否调整了缓冲区缓存的大小?在这个大的执行期间调整大小的小而冷的缓冲区缓存可能导致需要将块读入缓冲区缓存以便更新它们。

基于您展示的数据的一些想法...请让我们知道发生了什么!

答案 1 :(得分:1)

最近我遇到了哪个巨大的更新问题。我找到了基于并行流水线功能的良好解决方案,大大减少了更新时间。 我的命题并不完全是你提出的问题,但也许这种方法可以在几天的时间内为你提供短暂而稳定的时间:

  1. 创建类型:

    CREATE type test_num_arr AS TABLE of INTEGER;
    /
    
  2. 更新流水线功能(你可以调整):

    create or replace FUNCTION test_parallel_update (
    test_cur IN SYS_REFCURSOR
    ) 
    RETURN test_num_arr
    PARALLEL_ENABLE (PARTITION test_cur BY ANY)
    PIPELINED
    IS
    PRAGMA AUTONOMOUS_TRANSACTION;
    
    test_rec HUGE_TABLE1%ROWTYPE;
    TYPE num_tab_t IS TABLE OF NUMBER(38);
    
    pk_tab NUM_TAB_T;
    
    cnt INTEGER := 0;
    BEGIN
    LOOP
        FETCH test_cur BULK COLLECT INTO pk_tab LIMIT 1000;
        EXIT WHEN pk_tab.COUNT() = 0;
    
        FORALL i IN pk_tab.FIRST .. pk_tab.LAST
            UPDATE HUGE_TABLE1
            set   col = 1
            where  col1 > 'A'
            and    col2 < 'B'
            and    exists ( select 1
                            from   huge_table2 b
                            where  b.col3 = a.col3 
                           )
            AND ID = pk_tab(i);
    
        cnt := cnt + pk_tab.COUNT;
    END LOOP;
    
    CLOSE test_cur;
    
    COMMIT;
    PIPE ROW(cnt);
    RETURN;
    END;
    
  3. 最后,运行您的更新:

    SELECT * FROM TABLE(test_parallel_update(CURSOR(SELECT id FROM huge_table1)));
    
  4. 基于以下方法的方法: http://www.orafaq.com/node/2450

答案 2 :(得分:1)

要回答有关策略的问题,当然必须选择LIO。缓冲区中的行访问比磁盘操作快得多。

关于你的问题,看到执行时间好的第一天和最后几天没有。 如果你在columns = b.col3 a.col3上使用索引,并且表中有很多插入。也许它们已经过时了,所以你的查询不能再使用索引并读取更多的块。 因为在执行计划中,我们看到磁盘读数增加。

在这种情况下,有必要:

EXEC DBMS_STATS.gather_table_stats(schema,table_name);

您应该定期使用调度程序收集统计信息。取决于您的数据量变化。

您可以在白天安排收集索引统计信息:

DBMS_STATS.GATHER_INDEX_STATS

晚上:

DBMS_STATS.GATHER_TABLE_STATS

女巫收集表和列(和索引)统计信息。

除了关于可能性的问题之外,还有对数据模型的更改。在大容量分区表是减少IO的好方法。

希望ca可以提供帮助

答案 3 :(得分:1)

正如bubooal所说,我们无法帮助您完成2个表的执行计划和表结构。你能给我们这2条信息吗?

也许分区可以帮助您减少I / O.

另一种可能性是将两个表保留在缓存中。似乎缓冲区get的数量是相同的。因此,当查询挂起时,因为您的表不在缓冲区缓存中。为此你可以使用db_keep_cache_size并将你的表(或好的分区)固定在这个缓存中