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

     

  • 相关阅读:
    PsySH——PHP交互式控制台
    CentOS 6.5升级Python和安装IPython
    yii2 邮件发送
    Centos 6.5安装最新版谷歌浏览器-Chrome
    centos 6.5 设置屏幕保护
    PHP实现生成唯一编号(36进制的不重复编号)
    十位用户唯一ID生成策略
    0基础学java_for循环
    0基础学java_while循环
    0基础学java_逻辑变量 逻辑表达式 和条件句
  • 原文地址:https://www.cnblogs.com/yaoyangding/p/14784562.html
Copyright © 2011-2022 走看看