【问题标题】:Top 5 time-consuming SQL queries in OracleOracle 中最耗时的 5 个 SQL 查询
【发布时间】:2010-09-23 22:15:49
【问题描述】:

如何在 Oracle 中找到性能不佳的 SQL 查询?

Oracle 维护共享 SQL 区域的统计信息,每个 SQL 字符串 (v$sqlarea) 包含一行。 但是我们如何才能确定其中哪一个表现不佳呢?

【问题讨论】:

    标签: performance oracle


    【解决方案1】:

    以下查询返回执行大量磁盘读取的 SQL 语句(还包括违规用户和查询已运行的次数):

    SELECT t2.username, t1.disk_reads, t1.executions,
        t1.disk_reads / DECODE(t1.executions, 0, 1, t1.executions) as exec_ratio,
        t1.command_type, t1.sql_text
      FROM v$sqlarea t1, dba_users t2
      WHERE t1.parsing_user_id = t2.user_id
        AND t1.disk_reads > 100000
      ORDER BY t1.disk_reads DESC
    

    以 SYS 身份运行查询并根据您认为过多的磁盘读取次数调整磁盘读取次数(100,000 次对我有用)。

    我最近使用此查询来追踪在执行他们的语句之前拒绝利用Explain Plans 的用户。

    我在一本旧的 Oracle SQL 调优书中找到了这个查询(很遗憾我已经没有了),所以很抱歉,但没有注明出处。

    【讨论】:

      【解决方案2】:

      我从 askTom-Oracle 获得的完整信息。希望对你有帮助

      select * 
      from v$sql 
      where buffer_gets > 1000000 
      or disk_reads > 100000 
      or executions > 50000 
      

      【讨论】:

        【解决方案3】:

        我发现这个 SQL 语句是一个有用的起点(抱歉,我不能将其归于原作者;我在互联网的某个地方找到了它):

        SELECT * FROM
        (SELECT
            sql_fulltext,
            sql_id,
            elapsed_time,
            child_number,
            disk_reads,
            executions,
            first_load_time,
            last_load_time
        FROM    v$sql
        ORDER BY elapsed_time DESC)
        WHERE ROWNUM < 10
        /
        

        这会查找当前存储在 SQL 缓存中按经过时间排序的顶级 SQL 语句。语句会随着时间的推移从缓存中消失,因此当您在中午开始工作时尝试诊断昨晚的批处理作业可能没有什么好处。

        您也可以尝试按 disk_reads 和 executions 排序。执行很有用,因为一些糟糕的应用程序多次发送相同的 SQL 语句。此 SQL 假定您正确使用绑定变量。

        那么,你可以把sql_idchild_number的语句喂给这个宝宝吃:-

        SELECT * FROM table(DBMS_XPLAN.DISPLAY_CURSOR('&sql_id', &child));
        

        这显示了来自 SQL 缓存的实际计划和 SQL 的全文。

        【讨论】:

        • 你应该在选择中添加 elapsed_time 否则会很混乱。
        • 在内部查询中添加此 WHERE 子句以仅包括最近的慢查询: WHERE LAST_LOAD_TIME > '2020-04-10' 运行 APEX 时,这两个字段也很有用:模块、操作、
        【解决方案4】:

        您可以获取实例活动期间每次执行的平均缓冲区获取:

        SELECT username,
               buffer_gets,
               disk_reads,
               executions,
               buffer_get_per_exec,
               parse_calls,
               sorts,
               rows_processed,
               hit_ratio,
               module,
               sql_text
               -- elapsed_time, cpu_time, user_io_wait_time, ,
          FROM (SELECT sql_text,
                       b.username,
                       a.disk_reads,
                       a.buffer_gets,
                       trunc(a.buffer_gets / a.executions) buffer_get_per_exec,
                       a.parse_calls,
                       a.sorts,
                       a.executions,
                       a.rows_processed,
                       100 - ROUND (100 * a.disk_reads / a.buffer_gets, 2) hit_ratio,
                       module
                       -- cpu_time, elapsed_time, user_io_wait_time
                  FROM v$sqlarea a, dba_users b
                 WHERE a.parsing_user_id = b.user_id
                   AND b.username NOT IN ('SYS', 'SYSTEM', 'RMAN','SYSMAN')
                   AND a.buffer_gets > 10000
                 ORDER BY buffer_get_per_exec DESC)
         WHERE ROWNUM <= 20
        

        【讨论】:

          【解决方案5】:

          您可以通过以下方式找到磁盘密集型全表扫描:

          SELECT Disk_Reads DiskReads, Executions, SQL_ID, SQL_Text SQLText, 
             SQL_FullText SQLFullText 
          FROM
          (
             SELECT Disk_Reads, Executions, SQL_ID, LTRIM(SQL_Text) SQL_Text, 
                SQL_FullText, Operation, Options, 
                Row_Number() OVER 
                   (Partition By sql_text ORDER BY Disk_Reads * Executions DESC) 
                   KeepHighSQL
             FROM
             (
                 SELECT Avg(Disk_Reads) OVER (Partition By sql_text) Disk_Reads, 
                    Max(Executions) OVER (Partition By sql_text) Executions, 
                    t.SQL_ID, sql_text, sql_fulltext, p.operation,p.options
                 FROM v$sql t, v$sql_plan p
                 WHERE t.hash_value=p.hash_value AND p.operation='TABLE ACCESS' 
                 AND p.options='FULL' AND p.object_owner NOT IN ('SYS','SYSTEM')
                 AND t.Executions > 1
             ) 
             ORDER BY DISK_READS * EXECUTIONS DESC
          )
          WHERE KeepHighSQL = 1
          AND rownum <=5;
          

          【讨论】:

          • DISK_READS 不是磁盘读取的总数,所以不用乘以执行次数吗?
          【解决方案6】:

          这取决于您拥有的 oracle 版本,对于 9i 及以下 Statspack 是您所追求的,10g 及以上,您想要 awr ,这两个工具都会为您提供顶级 sql 和许多其他东西。

          【讨论】:

            【解决方案7】:

            在搜索时,我得到了以下查询,它在一个假设下完成了这项工作(查询执行时间 >6 秒)


            选择用户名、sql_text、sofar、totalwork、单位

            来自 v$sql,v$session_longops

            哪里 sql_address = 地址 AND sql_hash_value = hash_value

            ORDER BY address, hash_value, child_number;


            我认为上面的查询会列出当前用户的详细信息。

            欢迎评论!!

            【讨论】:

            • 此查询不限于当前用户,只有当查询出现在 v$session_longops 中时才有效。 Longops 记录了 Oracle 经过排序、表扫描、索引全扫描的距离。如果您的查询由于嵌套循环计划错误而变慢,则不会显示,因为没有 longops。
            【解决方案8】:

            有很多可能的方法可以做到这一点,但有一个 tkprof 的谷歌

            没有图形用户界面......它完全是命令行,对于 Oracle 初学者来说可能有点吓人;但它非常强大。

            这个链接看起来是一个好的开始:

            http://www.oracleutilities.com/OSUtil/tkprof.html

            【讨论】:

            • 有没有办法通过sql查询获取数据? Oracle 是否在某些系统表中维护相关数据?
            • 它在系统表中维护的数据不如使用 tkprof 获得的数据多。请参阅我的答案,以快速而肮脏地查找不良陈述。 tkprof 更好,但您需要专门设置一个测试并运行它。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-03-23
            • 2013-07-24
            相关资源
            最近更新 更多