【问题标题】:reduced buffer cache hits for a query causes random performance issues减少查询的缓冲区缓存命中会导致随机性能问题
【发布时间】:2014-07-29 16:01:07
【问题描述】:

我们在生产中(如下)有一个更新,它每天处理或多或少相同数量的行,但运行时却截然不同。有时查询会在 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

如上所示,尽管由于 IO 等待增加,第 3 天和第 4 天经过的时间增加了,但在每种情况下,LIO 几乎保持不变,如果我的假设是正确的,那是由 PIO 增加引起的。根据 Tom Kyte 的说法,调整的重点应该是减少 LIO 而不是 PIO,随着 LIO 的减少,PIO 也会减少。但在这种情况下,LIO 始终保持不变,而 PIO 却发生了显着变化。

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

【问题讨论】:

  • 我不确定您在这里所说的 LIO/PIO 是什么意思,但您专注于查询和调整策略似乎很奇怪。您知道查询不是问题。根据提供的信息,我同意您的分析,您的问题是 I/O 等待。那么,为什么要等这么久呢?由于查询不是问题,可能是什么环境因素导致了这个问题?同时还有什么在运行?
  • LIO/PIO 分别是逻辑/物理读取。我已经检查过了,除了这个 UPDATE 同时运行之外,没有什么重要的(即长时间运行/等待)。

标签: sql performance oracle oracle11g


【解决方案1】:

我愿意:

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

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

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

基于您展示的数据的一些想法...请让我们知道结果如何!

【讨论】:

  • 感谢您的回答。我检查了他们没有更改的计划,并且在运行此查询期间的 I/O 负载不是很高(峰值大约是 10 倍高)。我无法检查会话级别的领先读取事件,但在系统级别它是 db 顺序读取。
  • 我没搞清楚,你能告诉我: 1.- 什么是 I/O 、CPU 、内存统计信息,当性能好坏时。 2.- 顺序读取导致的系统级别,你从哪里得到这个?我假设 AWR,如果是这种情况,您能否分享 AWR 报告中的前 5 个事件。 3.- 会话级别,您应该能够获得与 awr 相同的快照的 ASH 报告,我相信 $ORACLE_HOME/admin/ 下的 ashrpt.sql... 4.- 如果可能...这将是最好的是,如果您可以针对性能的好坏执行扩展的 sql 跟踪,这将为您提供 str8 的答案。
  • 我的最后一条评论空间不足。扩展的 sql 跟踪也称为跟踪 10046 级别 12 是生产安全的,只要确保您只为运行此查询的会话启用它,您应该没有问题。请告诉我结果!
  • 我还没有解决它,但是我从 AWR 数据中得到了一些有趣的见解。如果我们解决了这个问题,会通知大家。
  • 每当我遇到这种事情时,最简单的方法总是观察好坏之间:)。这需要一段时间,但你最终会得到它,总是一次只专注一件事。
【解决方案2】:

最近我遇到了重大更新的问题。我找到了基于并行流水线功能的良好解决方案,可以显着减少更新时间。 我的提议与您所要求的不完全一样,但也许这种方法可以让您在几天内获得短暂而稳定的时间:

  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)));
    

方法基于: http://www.orafaq.com/node/2450

【讨论】:

  • 感谢您的宝贵时间。这是对 PIPE 功能的一个很好的使用,但我不知道它是否有助于减少 I/O。然而,基准测试结果令人印象深刻,因为它违背了 Tom 的口头禅:“如果可能,您应该在单个 SQL 语句中完成它。” tkyte.blogspot.in/2006/10/slow-by-slow.html 公平地说,这是并行 MERGE 而不是并行 UPDATE。
  • 再仔细阅读那篇文章。 不是说并行流水线函数比并行 DML 更快。流水线函数更快的情况是由不同程度的并行引起的。显然,如果使用不同的 DOP 运行,性能会有所不同。该网站通常是可靠的,但这篇文章非常具有误导性。汤姆的口头禅仍然是正确的。
  • jonearles:是的,你是对的,我没有在字里行间。 :-)
【解决方案3】:

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

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

在这种情况下,有必要:

EXEC DBMS_STATS.gather_table_stats(schema, table_name);

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

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

DBMS_STATS.GATHER_INDEX_STATS

晚上:

DBMS_STATS.GATHER_TABLE_STATS

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

除了您关于可能性的问题之外,数据模型也发生了变化。在大容量分区表上是减少 IO 的好方法。

希望能帮到你

【讨论】:

  • OP 使用的是 Oracle 11g;统计数据会在一夜之间自动收集。
  • 如果您谈到 AutoTask,它只是默认启用。这并不意味着 OP 没有停用。
  • Bertrand Ring:Ben 是正确的,我们自动收集了最新的 STATS。
【解决方案4】:

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-17
    • 1970-01-01
    • 1970-01-01
    • 2022-01-20
    • 1970-01-01
    相关资源
    最近更新 更多