【发布时间】:2010-09-23 22:15:49
【问题描述】:
如何在 Oracle 中找到性能不佳的 SQL 查询?
Oracle 维护共享 SQL 区域的统计信息,每个 SQL 字符串 (v$sqlarea) 包含一行。 但是我们如何才能确定其中哪一个表现不佳呢?
【问题讨论】:
标签: performance oracle
如何在 Oracle 中找到性能不佳的 SQL 查询?
Oracle 维护共享 SQL 区域的统计信息,每个 SQL 字符串 (v$sqlarea) 包含一行。 但是我们如何才能确定其中哪一个表现不佳呢?
【问题讨论】:
标签: performance oracle
以下查询返回执行大量磁盘读取的 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 调优书中找到了这个查询(很遗憾我已经没有了),所以很抱歉,但没有注明出处。
【讨论】:
我从 askTom-Oracle 获得的完整信息。希望对你有帮助
select *
from v$sql
where buffer_gets > 1000000
or disk_reads > 100000
or executions > 50000
【讨论】:
我发现这个 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_id和child_number的语句喂给这个宝宝吃:-
SELECT * FROM table(DBMS_XPLAN.DISPLAY_CURSOR('&sql_id', &child));
这显示了来自 SQL 缓存的实际计划和 SQL 的全文。
【讨论】:
您可以获取实例活动期间每次执行的平均缓冲区获取:
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
【讨论】:
您可以通过以下方式找到磁盘密集型全表扫描:
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;
【讨论】:
在搜索时,我得到了以下查询,它在一个假设下完成了这项工作(查询执行时间 >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;
我认为上面的查询会列出当前用户的详细信息。
欢迎评论!!
【讨论】:
有很多可能的方法可以做到这一点,但有一个 tkprof 的谷歌
没有图形用户界面......它完全是命令行,对于 Oracle 初学者来说可能有点吓人;但它非常强大。
这个链接看起来是一个好的开始:
【讨论】: