ITPub博客

首页 > Linux操作系统 > Linux操作系统 > 【转】 sql_trace

【转】 sql_trace

原创 Linux操作系统 作者:yellowlee 时间:2009-05-19 20:50:53 0 删除 编辑
前言:
sql_trace 是我在工作中经常要用到的调优工具,
相比较statspack 我更愿意用这个工具。
因为数据库慢原因的85%以上是由于sql问题造成的,
statspack没有sql的执行计划。显示没有它直观,方便,对想要针对性不强,

1,介绍
数据库调优需要经常会用到的工具,可以很精确地跟抓取相关session正在运行的sql。再通过tkprof分析出来sql的执行计划等相关信息,从而判断那些sql语句存在问题。

统计如下信息(摘字官方文档):
Parse, execute, and fetch counts
CPU and elapsed times
Physical reads and logical reads
Number of rows processed
Misses on the library cache
Username under which each parse occurred
Each commit and rollback

2,使用
使用前需要注意的地方
1,初始化参数
timed_statistics=true  允许sql trace 和其他的一些动态性能视图收集与时间(cpu,elapsed)有关的参数。一定要打开,不然相关信息不会被收集。这是一个动态的参数,也可以在session级别设置。
SQL>alter session set titimed_statistics=true
2,MAX_DUMP_FILE_SIZE
跟踪文件的大小的限制,如果跟踪信息较多可以设置成unlimited。可以是KB,MB单位,9I开始默认为unlimited
这是一个动态的参数,也可以在session级别设置。
SQL>alter system set max_dump_file_size=300
SQL>alter system set max_dump_file_size=unlimited
3,USER_DUMP_DEST
指定跟踪文件的路径,默认路径实在$ORACLE_BASE/admin/ORA_SID/udump
这是一个动态的参数,也可以在session级别设置。
SQL>alter system set user_dump_dest=/oracle/trace

数据库级别
设置slq_trace参数为true会对整个实例进行跟踪,包括所有进程:用户进程和后台进程,会造成比较严重的性能问题,生产环境一定要慎用。
SQL>alter system set sql_trace=true;

Session级别:
当前会话:
SQL>alter session set sql_trace=true;
SQL>alter session set sql_trace=false;
其他会话:
通过oracle提供的系统包 DBMS_SYSTEM.SET_SQL_TRACE_IN_SESSION来实现。
SQL>execute dbms_system.set_sql_trace_in_session(sid,serial#,true);
SQL>execute dbms_system.set_sql_trace_in_session(sid,serial#,false);
注:
1,sid,serial#从v$session视图中获得。
2,DBMS_SYSTEM包里还可以对其他用户的参数(如:timed_statistics,max_dump_file)进行设置,在这不做介绍了,很少用到。想了解dbms_system里的程序包可以desc dbms_system看一下。
得到trace文件后我们要用tkprof他进行格式化,通过sql语句快速定位到相应的trace文件
Tkprof
tkprof 的目的是将sql trace 生成的跟踪文件转换成用户可以理解的格式
格式:
tkprof tracefile outputfile [optional | parameters ]
参数和选项(这里只介绍最常用的,也是最实用的)
explain=user/password 执行explain命令将结果放在SQL trace的输出文件中
sys=[yes/no] 确定系统是否列出由sys用户产生或重调的sql语句
sort=sort_option 按照指定的方法对sql trace的输出文件进行降序排序
sort_option 选项
prscnt  按解析次数排序
prscpu  按解析所花cpu时间排序
prsela  按解析所经历的时间排序
prsdsk  按解析时物理的读操作的次数排序
prsqry  按解析时以一致模式读取数据块的次数排序
prscu   按解析时以当前读取数据块的次数进行排序
execnt  按执行次数排序
execpu  按执行时花的cpu时间排序
exeela  按执行所经历的时间排序
exedsk  按执行时物理读操作的次数排序
exeqry  按执行时以一致模式读取数据块的次数排序
execu   按执行时以当前模式读取数据块的次数排序
exerow  按执行时处理的记录的次数进行排序
exemis  按执行时库缓冲区的错误排序
fchcnt  按返回数据的次数进行排序
fchcpu  按返回数据cpu所花时间排序
fchela  按返回数据所经历的时间排序
fchdsk  按返回数据时的物理读操作的次数排序
fchqry  按返回数据时一致模式读取数据块的次数排序
fchcu   按返回数据时当前模式读取数据块的次数排序
fchrow  按返回数据时处理的数据数量排序
注:
这些排序中我经常用到的是fchdsk,fckchela ,fchqry.因为有问题的sql一般都是大的查询造成的,当然更新,插入,删除时也会存在全表扫描,这就需要:exedsk,exeqry,exeela等选项。根据具体情况具体分析。
Cpu时间和Elapsed时间都是以秒为单位,而且两个值基本上一样,但我比较常用elapsed,他是反映的用户相应时间,从运行sql到用户得到结果的时间,会更实际些。

tkprof输出文件各列的含义:(理解下面的含义对我们快速定位问题很有帮助)
parse:
将sql语句转换成执行计划,包括检查是否有正确的授权,需要到得表,列及其他引用到得对象是否存在,这些信息分别存在v$librarycache.v$rowcache..
execute
oracle实际执行的语句,如:insert,update,delete,这些会修改数据,对于select操作,这部只是确定选择的行数。
fetch
返回查询获得的行数,只有执行select会被收集。
Count
这个语句被parse,execute,fetch的次数的统计
Cpu
这个语句所有的parse,execute,fetch所用的cpu总的时间,以秒为单位。如果TIMED_STATISTICS 关闭的话,值为0。
Elapsed
这个语句所有的parse,execute,fetch所消耗的总的时间,以秒为单位。如果TIMED_STATISTICS 关闭的话,值为0。
Disk
这个语句所有的parse,execute,fetch从磁盘上的数据文件中读取的数据块的数量
Query
在一致性读的模式下,这个语句所有的parse,execute,fetch所获取的buffer数量(这部分是从内存读取的也就是逻辑读取的,相当于执行计划里的consistent gets)
Current
在current模式下,这个语句所有的parse,execute,fetch所获取的buffer数量,一般是current模式下发生的delect,insert,update的操作都会获取buffer。
Rows
语句返回的行数,不包括子查询中返回的记录数目。对于select语句,返回在fetch这步,对于insert,delete,update操作,返回记录是在execute这步。

3,分析:
我一般的思路步骤是:
1,现找磁盘多的sq l(sort= fchdsk ),意味着全表扫描
2,找运行时间长的(sort= fchela),意味着sql可能写的不好或磁盘,逻辑读较多
3,找出一致性读较多的(sort= fchqry),当表不是很大的时候(可能全部缓存住了),没有发生磁盘读,但不意味着不需要建立索引,或者sql需要优化
4,找出当前模式从缓冲区获得数据的数量(sort=exedsk,exeela,exeqry),这些主要集中在dml语句里的操作,看是否有必要优化sql或建立索引
之所以排序是为了在sql很多的时候快速定位sql,如果sql比较少的话就没必要排序了,
但我们要有分析问题的思路。

4,举例:
我自己建立了一个表
create table t1 (id int);
begin
for v1 in 1..1000000 loop
insert into t1 values(v1);
end loop
commit;
end;

下面是sql_trace所抓到得sql.

不正常状态
*******************************************************************************
select *
from  t1
where id=1

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        5      0.00       0.00          0          0          0           0
Execute      5      0.00       0.00          0          0          0           0
Fetch       10      1.23       9.64       4387       9062          0           5
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total       20      1.23       9.64       4387       9062          0           5

Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 258  (WH)
Rows     Row Source Operation
-------  ---------------------------------------------------
      1  TABLE ACCESS FULL T1
Rows     Execution Plan
-------  ---------------------------------------------------
      0  SELECT STATEMENT   GOAL: CHOOSE
      1   TABLE ACCESS (FULL) OF 'T1'

*******************************************************************************
首先这是一个select语句,
它走了全部扫描,
磁盘读(4387)和逻辑读(9062)都很多
运行了5次(Execute),分析了5次(Parse),一共用了将近10秒(elapsed)。
我只是选择表的一行的数据的结果,就发生这么大的成本。很显然是全表扫描的结果造成的。

正常状态:
在做跟踪前 我为这个表建立了一个索引
Create index t1 on t1(id);
*******************************************************************************
select *
from t1
where id=1

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1     0.01       0.00          0          0          0           0
Execute      5      0.00       0.00          0          0          0           0
Fetch        5      0.00       0.02          3         20          0           5
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total       11     0.01       0.03          3         20          0           5
Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 258  (WH)

Rows     Row Source Operation
-------  ---------------------------------------------------
      1  INDEX RANGE SCAN T1 (object id 9491829)

Rows     Execution Plan
-------  ---------------------------------------------------
      0  SELECT STATEMENT   GOAL: CHOOSE
      1   INDEX (RANGE SCAN) OF 'T1' (NON-UNIQUE)

*******************************************************************************

同样的语句
它走了索引,
物理读3 这个3其实是开始读索引时需要第一次读入的。以后运行就没有了。
逻辑读20(平均这个sql一次4个逻辑读)
同样运行了5次(Execute)
分析了1次(Parse) 运行次数越多,分析次数越少越好
一共只用了0.03秒(elapsed)。

可以看出前后很大的差距。


5,注意:
电脑学习网首发

执行计划走了索引,不一定在实际应用中就走了,我们一定要结合磁盘读,一致性读,运行时间,返回行数等信息来确定是否真正走了索引,可能存在索引失效的现象。关于索引为什么失效可以看我另外一个文章,写的比较全面:
http://www.cnscn.org/htm_data/586/0904/46107.html

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/16179598/viewspace-600492/,如需转载,请注明出处,否则将追究法律责任。

上一篇: out of spring
请登录后发表评论 登录
全部评论

注册时间:2008-12-27

  • 博文量
    316
  • 访问量
    658377