zoukankan      html  css  js  c++  java
  • oracle sql语句跟踪及性能分析工具实现

    oracle sql语句跟踪及性能分析工具实现

     

    在网上找了一大圈,没找着合适的工具来跟踪oracle一段时间的sql。

    我们的场景是打算自动化跑遍所有场景(rft)+fiddler跟踪请求+后端跟踪sql,根据结果去分析慢的请求和sql,本来awr报告也能实现,但是每次都用awr比较麻烦,想的是能够简单点,直接定位到执行慢的sql或者耗cpu的sql。

    这个工具抓取的sql不是实时的,是某段时间的sql,原理也很简单,比awr的sql要简单的多,试了一把,跟awr给出的数据是一致的。

    最终效果:准备操作时开启跟踪,结束操作后关闭跟踪,然后工具会提供这段时间的sql及相关指标,包括磁盘读次数、cpu、sql执行时间、解析次数、执行次数等,根据需要可以把跟踪结果保存到数据库或者导出到excel。

    视图介绍

    用到的视图:v$sqlarea

    这个视图存放了近期执行过的sql,如果想要达到awr的效果,可以再结合dba_hist_sqlstat等相关视图进行查询。

    本次用到的相关字段:

    SQL_FULLTEXT:完整的sql语句,类型为lob字段,要获取内容可以使用DBMS_LOB.SUBSTR(Sql_Fulltext,2000,1)进行读取,获取sql前2000个字符;

    PARSING_SCHEMA_NAME:执行sql的用户;

    MODULE:执行sql的应用程序,如IIS为w3wp.exe,plsql为PL/SQL Developer;

    PARSE_CALLS:解析次数,包括软解析和硬解析;

    EXECUTIONS:sql执行次数;

    BUFFER_GETS:读内存次数;

    DISK_READS:读磁盘次数(可以结合读内存次数计算命中率);

    CPU_TIME:cpu时间,微秒;

    ELAPSED_TIME:sql执行时间,微秒;

    LAST_ACTIVE_TIME:sql上次的活动时间。

    由于v$sqlarea存放的数据都为累加值,所以要计算本次的数据的话,需要减去之前的旧数据。

    实现原理

    开始执行操作前,备份v$sqlarea,存放到oldtable;

    获取当前系统时间,存放到sysdate;

    结束操作后,备份v$sqlarea,存放到newtable。至此,准备工作就做完了,接下来就是整合数据了。

    根据newtable中的last_active_time>sysdate获取本次执行过的sql,然后相关的指标数据都是newtable减去oldtable(两张表的关联字段为hash_value和address,sql_id应该也可以)。

    要注意newtable中新出现的sql,所以用left join并用nvl转换一下空值。如果关心其他指标,可以根据实际情况添加字段。

    如果要避免其他因素的干扰,可以只显示符合要求的sql,比如PARSING_SCHEMA_NAME=登陆用户或者MODULE=w3wp.exe等。

    核心sql:

    sql = "SELECT * FROM (SELECT n.parsing_schema_name AS SCHEMA,n.module AS MODULE, DBMS_LOB.SUBSTR(n.Sql_Fulltext,2000,1) AS SQL_TEXT,n.parse_calls-nvl(o.parse_calls,0) AS PARSE_CALLS,n.buffer_gets-nvl(o.buffer_gets,0) AS BUFFER_GETS,n.disk_reads-nvl(o.disk_reads,0) AS DISK_READS,n.executions-nvl(o.executions,0) AS EXECUTIONS,round((n.cpu_time-nvl(o.cpu_time,0))/1000000,2) AS CPU_TIME,round((n.cpu_time-nvl(o.cpu_time,0))/((n.executions-nvl(o.executions,0))*1000000),2) AS CPU_TIME_PER_EXE,round((n.elapsed_time-nvl(o.elapsed_time,0))/((n.executions-nvl(o.executions,0))*1000000),2) AS ELAPSED_TIME_PER_EXE " +
    "FROM "+NewTable+" n " +
    "LEFT JOIN "+OldTable+" o ON o.hash_value = n.hash_value AND o.address = n.address " +
    "WHERE n.last_active_time > to_date('" + Sysdate + "','yyyy/MM/dd hh24:mi:ss') " +
    "AND(n.executions - nvl(o.executions,0)) > 0 " +
    "ORDER BY ELAPSED_TIME_PER_EXE DESC) WHERE ROWNUM<=20";

    最终效果(c#版):

     2017.11更新

    后续修改了下,去掉了导出excel的功能,改为保存到数据库了。

    然后把sqlserver的也写了下,跟sqlserver profiler比对了下,大致差不多,但是总是有个1s左右的出入,不知道啥原因,也是醉了。

    可能是方法不对吧。。。

    附上sqlserver的核心sql:

    sql = "select top 20 ST.text as SQL_TEXT,cast((n.total_worker_time-isnull(o.total_worker_time,0))*1.0/(n.execution_count-isnull(o.execution_count,0)) as decimal(10,2)) as PER_CPU_TIME,cast((n.total_physical_reads-isnull(o.total_physical_reads,0))*1.0/(n.execution_count-isnull(o.execution_count,0)) as decimal(10,2)) as PHYSICAL_READS,cast((n.total_logical_reads-isnull(o.total_logical_reads,0))*1.0/(n.execution_count-isnull(o.execution_count,0)) as decimal(10,2)) as LOGICAL_READS,cast((n.total_logical_writes-isnull(o.total_logical_writes,0))*1.0/(n.execution_count-ISNULL(o.execution_count,0)) as decimal(10,2)) as LOGICAL_WRITES,n.execution_count-isnull(o.execution_count,0) as EXECUTIONS,cast(n.last_elapsed_time*1.0/1000000 as decimal(10,2)) as LAST_EXEC_TIME,cast((n.total_elapsed_time-isnull(o.total_elapsed_time,0))*1.0/((n.execution_count-isnull(o.execution_count,0))*1000000) as decimal(10,2)) as PER_EXEC_TIME from " + SqlNewTable+" n "+
    "left join "+SqlOldTable+" o on o.plan_handle = n.plan_handle "+
    "CROSS APPLY "+
    "sys.dm_exec_sql_text(n.sql_handle) ST "+
    "where n.last_execution_time > '"+SqlSysdate+"' and n.creation_time < '"+SqlSysdate+"' "+
    "and n.execution_count - o.execution_count > 0 "+
    "union "+
    "select ST.text as SQL_TEXT,cast(n.total_worker_time * 1.0 / n.execution_count as decimal(10, 2)) as PER_CPU_TIME,cast(n.total_physical_reads * 1.0 / n.execution_count as decimal(10, 2)) as PHYSICAL_READS,cast(n.total_logical_reads * 1.0 / n.execution_count as decimal(10, 2)) as LOGICAL_READS,cast(n.total_logical_writes * 1.0 / n.execution_count as decimal(10, 2)) as LOGICAL_WRITES,n.execution_count as EXECUTIONS,cast(n.last_elapsed_time * 1.0 / 1000000 as decimal(10, 2)) as LAST_EXEC_TIME,cast(n.total_elapsed_time * 1.0 / (n.execution_count * 1000000) as decimal(10, 2)) as PER_EXEC_TIME from " + SqlNewTable+" n "+
    "CROSS APPLY "+
    "sys.dm_exec_sql_text(n.sql_handle) ST "+
    "where n.creation_time > '"+SqlSysdate+"' "+
    "order by PER_EXEC_TIME desc";

     

  • 相关阅读:
    LeetCode 24. Swap Nodes in Pairs (两两交换链表中的节点)
    LeetCode 1041. Robot Bounded In Circle (困于环中的机器人)
    LeetCode 1037. Valid Boomerang (有效的回旋镖)
    LeetCode 1108. Defanging an IP Address (IP 地址无效化)
    LeetCode 704. Binary Search (二分查找)
    LeetCode 744. Find Smallest Letter Greater Than Target (寻找比目标字母大的最小字母)
    LeetCode 852. Peak Index in a Mountain Array (山脉数组的峰顶索引)
    LeetCode 817. Linked List Components (链表组件)
    LeetCode 1019. Next Greater Node In Linked List (链表中的下一个更大节点)
    29. Divide Two Integers
  • 原文地址:https://www.cnblogs.com/yaoyangding/p/14784562.html
Copyright © 2011-2022 走看看