技术开发 频道

SQL语句性能调整之注意事项

  【IT168 技术文档】

  1. 不要认为将optimizer_mode参数设为rule,就认为所有的语句都使用基于规则的优化器

  不管optimizer_mode参数如何设置,只要满足下面3个条件,就一定使用CBO。

  1) 如果使用Index Only Tables(IOTs), 自动使用CBO.

  2) Oracle 7.3以后,如果表上的Paralle degree option设为>1,

  则自动使用CBO, 而不管是否用rule hints.

  3) 除rlue以外的任何hints都将导致自动使用CBO来执行语句

  总结一下,一个语句在运行时到底使用何种优化器可以从下面的表格中识别出来,从上到下看你的语句到底是否满足description列中描述的条件:

  Description 对象是否被分析 优化器的类型

  ~~~~~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~~~

  Non-RBO Object (Eg:IOT) n/a #1

  Parallelism > 1 n/a #1

  RULE hint n/a RULE

  ALL_ROWS hint n/a ALL_ROWS

  FIRST_ROWS hint n/a FIRST_ROWS

  *Other Hint n/a #1

  OPTIMIZER_GOAL=RULE n/a RULE

  OPTIMIZER_GOAL=ALL_ROWS n/a ALL_ROWS

  OPTIMIZER_GOAL=FIRST_ROWS n/a FIRST_ROWS

  OPTIMIZER_GOAL=CHOOSE NO RULE

  OPTIMIZER_GOAL=CHOOSE YES ALL_ROWS

  #1 表示除非OPTIMIZER_GOAL 被设置为FIRST_ROWS ,否则将使用ALL_ROWS。在PL/SQL中,则一直是使用ALL_ROWS

  *Other Hint 表示是指除RULE、ALL_ROWS 和FIRST_ROWS以外的其它提示

  2) 当CBO选择了一个次优化的执行计划时, 不要同CBO过意不去, 先采取如下措施:

  a) 检查是否在表与索引上又最新的统计数据

  b) 对所有的数据进行分析,而不是只分析一部分数据

  c) 检查是否引用的数据字典表,在oracle 10G之前,缺省情况下是不对数据字典表进行分析的。

  d) 试试RBO优化器,看语句执行的效率如何,有时RBO能比CBO产生的更好的执行计划

  e) 如果还不行,跟踪该语句的执行,生成trace信息,然后用tkprof格式化trace信息,这样可以得到全面的供优化的信息。

  3) 假如利用附录的方法对另一个会话进行trace,则该会话应该为专用连接

  4) 不要认为绑定变量(bind variables)的缺点只有书写麻烦,而优点多多,实际上使用绑定

  变量虽然避免了重复parse,但是它导致优化器不能使用数据库中的列统计,从而选择了较差的执行计划。而使用硬编码的SQL则可以使用列统计。当然随着CBO功能的越来越强,这种情况会得到改善。目前就已经实现了在第一次运行绑定变量的sql语句时,考虑列统计。

  5) 如果一个row source 超过10000行数据,则可以被认为大row source

  6) 有(+)的表不是driving table,注意:如果有外联接,而且order hint指定的顺序与外联结决定的顺序冲突,则忽略order hint

  7) 影响CBO选择execution plan的初始化参数:

  这些参数会影响cost值

  ALWAYS_ANTI_JOIN

  B_TREE_BITMAP_PLANS

  COMPLEX_VIEW_MERGING

  DB_FILE_MULTIBLOCK_READ_COUNT

  FAST_FULL_SCAN_ENABLED

  HASH_AREA_SIZE

  HASH_JOIN_ENABLED

  HASH_MULTIBLOCK_IO_COUNT

  OPTIMIZER_FEATURES_ENABLE

  OPTIMIZER_INDEX_CACHING

  OPTIMIZER_INDEX_COST_ADJ

  OPTIMIZER_MODE> / GOAL

  OPTIMIZER_PERCENT_PARALLEL

  OPTIMIZER_SEARCH_LIMIT

  PARTITION_VIEW_ENABLED

  PUSH_JOIN_PREDICATE

  SORT_AREA_SIZE

  SORT_DIRECT_WRITES

  SORT_WRITE_BUFFER_SIZE

  STAR_TRANSFORMATION_ENABLED

  V733_PLANS_ENABLED

  CURSOR_SHARING

  附录:

  如何通过跟踪一个客户端程序发出的sql的方法来优化SQL

  简要说来,跟踪一个客户程序发出的SQL主要分成下面几步:

  1) 识别要跟踪的客户端程序到数据库的连接(后面都用session代替),主要找出能唯一识别一个session的sid与serial#.

  2) 设定相应的参数,如打开时间开关(可以知道一个sql执行了多长时间),存放跟踪数据的文件的位置、最大值。

  3) 启动跟踪功能

  4) 让系统运行一段时间,以便可以收集到跟踪数据

  5) 关闭跟踪功能

  6) 格式化跟踪数据,得到我们易于理解的跟踪结果。

  现在就每一步,给出详细的说明:

  1) 识别要跟踪的客户端程序到数据库的数据库连接

  查询session信息(在sql*plus中运行):

  set linesize 190

  col machine format a30 wrap

  col program for a40

  col username format a15 wrap

  set pagesize 500

  select s.sid sid, s.SERIAL# "serial#", s.username, s.machine, s.program,

  p.spid ServPID, s.server

  from v$session s, v$process p

  where p.addr = s.paddr ;

  如得到的一个查询结果如下:

  SID serial# USERNAME MACHINE PROGRAM SERVPID SERVER

  ---- ------- -------- ------------------ ------------- --------- ---------

  8 3 SCOTT WORKGROUP\SUNNYXU SQLPLUS.EXE 388 DEDICATED

  LOGON_TIME

  ------------------

  2005.06.28 18:50:11

  上面的结果中比较有用的列为:

  sid, serial# : 这两个值联合起来唯一标识一个session

  username : 程序连接数据库的用户名

  machine : 连接数据库的程序所在的机器的机器名,可以hostname得到

  program : 连接数据库的程序名,所有用java jdbc thin的程序的名字都一样,

  servpid : 与程序对应的服务器端的服务器进程的进程号,在unix下比较有用

  server : 程序连接数据库的模式:专用模式(dedicaed)、共享模式(shared)。 55

  只有在专用模式下的数据库连接,对其进程跟踪才有效

  logon_time : 程序连接数据库的登陆时间

  根据machine, logon_time 可以方便的识别出一个数据库连接对应的session,从而得到该sesion的唯一标识sid, serial#, 为对该session进行跟踪做好准备

  2) 设定相应的参数

  参数说明:

  timed_statistics : 收集跟踪信息时,是否将收集时间信息,如果收集,

  则可以知道一个sql的各个执行阶段耗费的时间情况

  user_dump_dest : 存放跟踪数据的文件的位置

  max_dump_file_size : 放跟踪数据的文件的最大值,防止由于无意的疏忽,

  使跟踪数据的文件占用整个硬盘,影响系统的正常运行

  设置的方法:

  SQL> exec sys.dbms_system.set_bool_param_in_session( -

  sid => 8, -

  serial# => 3, -

  parnam => 'timed_statistics', -

  bval => true);

  SQL> alter system set user_dump_dest='c:\temp';

  -- 注意这个语句会改变整个系统的跟踪文件存放的位置,所以我一般不改这个参数,而用系统的缺省值,要查看当前系统的该参数的值,可以用system用户登陆后:

  SQL> show parameter user_dump_dest

  SQL> exec sys.dbms_system.set_int_param_in_session( -

  sid => 8, -

  serial# => 3, -

  parnam => 'max_dump_file_size', -

  intval => 2147483647)

  3) 启动跟踪功能

  SQL> exec sys.dbms_system.set_sql_trace_in_session(8, 3, true);

  注意,只有跟踪的session再次发出sql语句后,才会产生trc文件

  4) 让系统运行一段时间,以便可以收集到跟踪数据

  5) 关闭跟踪功能

  SQL> exec sys.dbms_system.set_sql_trace_in_session(8,3,false);

  6) 格式化跟踪数据,得到我们易于理解的跟踪结果。

  对产生的trace文件进行格式化:

  在命令提示符下,运行下面的命令 56

  tkprof dsdb2_ora_18468.trc dsdb2_trace.out SYS=NO EXPLAIN=SCOTT/TIGER

  其它使用tkprof的例子:

  (a) tkprof tracefile.trc sort_1.prf explain=apps/your_apps_password print=10 sort='(prsqry,exeqry,fchqry,prscu,execu,fchcu)' (b) tkprof tracefile.trc sort_2.prf explain=apps/your_apps_password print=10 sort='(prsela,exeela,fchela)' (c) tkprof tracefile.trc sort_3.prf explain=apps/your_apps_password print=10 sort='(prscnt,execnt,fchcnt)' (d) tkprof tracefile.trc normal.prf explain=apps/your_apps_password

  现对tkprof程序做进一步的说明:

  在打开跟踪功能后,oracle将被跟踪session中正在执行的SQL的性能状态数据都收集到一个跟踪文件中。这个跟踪文件提供了许多有用的信息,例如一个sql的解析次数、执行次数、fetch次数、物理读次数、逻辑读次数、CPU使用时间等,利用这些信息可以诊断你的sql的问题,从而用来优化你的系统。不幸的是,生成的跟踪文件中的数据是我们难以理解的,所以要用TKPROF工具对其进行转换,转换成我们易于理解格式。tkprof是oracle提供的实用工具,类似于sql*plus,在安装完oracle客户端后就自动安装到系统中,直接在命令符下用就可以了。

  当在打开跟踪功能时发生了recursive calls,则tkprof也会产生这些recursive calls的统计信息,并清楚的在格式化输出文件中标名它们为recursive calls。

  注意:recursive calls的统计数据是包含在recursive calls上的,并不包含在引起该recursive calls语句的sql语句上面。所以计算一个sql语句耗费的资源时,也要考虑该sql语句引起recursive calls语句花费的资源。通过将sys参数设为no时,我们变可以在格式化的输出文件中屏蔽掉这些recursive calls信息。

  如何得到tkprof的帮助信息

  运行tkprof时,不带任何参数,就可以得到该工具的帮助信息。

  执行计划:

  ---------------

  一个语句的执行计划是oracle执行这个sql语句的一系列指令。通过检验执行计划,你可以更好的知道oracle如何执行你的sql语句,这个信息可以帮助你决定是否你写的sql语句已经使用了索引。

  如果在tkprof中指定了EXPLAIN参数,tkprof使用EXPLAIN PLAN命令来为每个被跟踪的sql语句产生执行计划。

  使用说明:

  TKPROF工具接受一个trace文件作为输入文件,利用提供给命令的多个参数对trace文件进行分析,然后将格式化好的结果放到一个输出文件中。

  TKPROF的使用语法:

  ----------------

  TKPROF command ::=

  >>-- TKPROF traced_file formatted_file ---------------------------------------------->

  | |

  +- SORT = ---------------------------------+

  | |

  +-- OPTION --+

  | |

  | +---- , ----+ |

  | V | |

  |__( OPTION )__|

  >----------------------------------------------------------------------------->

  | | | | | |

  +-- PRINT = integer --+ +-- INSERT = filname3 --+ +-- SYS = ---------+

  | |

  +- YES -+

  | |

  +- NO --+

  >----------------------------------------------------------------------------->

  | |

  +---------------------------------------- EXPLAIN = user/password ------+

  | |

  +---- TABLE = schema.table ----+

  >----------------------------------------------------------------------------><

  | |

  +---- RECORD = filname ----+

  各个参数的含义:

  ' traced_file '

  指定输入文件,即oracle产生的trace文件,该文件中可以只包含一个

  session的跟踪信息,也可以包含系统中所有session的信息(此时需要在系统级进行跟踪)

  'formatted_file'

  指定输出文件,即我们想得到的易于理解的格式化文件,我们利用该文件

  对会话运行的sql进行分析。

  'EXPLAIN'

  利用哪个用户对trace文件中的sql进行分析,从而得到该sql语句的

  执行计划,这也说明在trace file中并没有各个sql语句的执行计划,只是在运

  行tkprof程序时才将trace file文件中的sql语句用explian参数指定的

  用户连接到数据库,然后运用EXPLAIN PLAN命令生成sql的执行计划。

  这个用户一般是你的程序中连接数据库的用户

  'TABLE'

  在对sql语句进行分析时,将产生的执行计划暂时存放到该表中。

  一般不需要该参数,这样当表不存在时,tkprof会自动创建相应的表,

  并在分析完之后,将创建的表自动删除。如果要指定自定义的表,该表的结构

  必须与utlxplan.sql文件中指定的表的结构一样。

  我一般不设置这个参数,让其采用默认的表名,并自动创建、删除

  'SYS'

  是否对sys用户运行的sql语句或被跟踪session产生的recursive SQL

  也进行分析,并将分析结果放到输出文件中。缺省值为YES。

  我一般设为NO,这样输出文件中只包含我发出的sql语句,

  而不包含系统产生的sql。

  SORT

  按照指定的排序选项(条件)对格式化好的sql语句进行降序排列,然后存放

  到输出文件中。可以将多个排序选项组合起来,如果没有指定排序选项,

  则按照使用sql的先后顺序。

  排序选项有:

  prscnt number of times parse was called

  prscpu cpu time parsing

  prsela elapsed time parsing

  prsdsk number of disk reads during parse

  prsqry number of buffers for consistent read during parse

  prscu number of buffers for current read during parse

  prsmis number of misses in library cache during parse

  execnt number of execute was called

  execpu cpu time spent executing

  exeela elapsed time executing

  exedsk number of disk reads during execute

  exeqry number of buffers for consistent read during execute

  execu number of buffers for current read during execute

  exerow number of rows processed during execute

  exemis number of library cache misses during execute

  fchcnt number of times fetch was called

  fchcpu cpu time spent fetching

  fchela elapsed time fetching

  fchdsk number of disk reads during fetch

  fchqry number of buffers for consistent read during fetch

  fchcu number of buffers for current read during fetch

  fchrow number of rows fetched

  userid userid of user that parsed the cursor

  PRINT

  只列出指定数量的已排序的sql语句,排序的条件参见SORT参数。

  如果忽略此参数,tkprof将跟踪文件中的所有的sql语句及其相关的

  分析数据存放到输出文件中。

  Print与sort参数组合在一起,可以实现:

  找出某一阶段耗费cpu最多的前n个sql

  找出某一阶段读硬盘最多的前n个sql等等。

  INSERT

  创建一个sql脚本文件,里面包含create table 与insert语句。

  利用这个脚本文件创建一个表及插入数据后,可以得到跟踪文件中

  所有sql语句(包含recursive SQL)的统计信息。如

  ,depth,user_id,

  parse_cnt,parse_cpu,parse_elap,parse_disk,

  parse_query,parse_current,parse_miss

  ,exe_count,exe_cpu,exe_elap,exe_disk,exe_query,

  exe_current,exe_miss,exe_rows

  ,fetch_count,fetch_cpu,fetch_elap,fetch_disk,

  fetch_query,fetch_current,fetch_rows,ticks

  ,sql_statement。

  利用这些信息,也可以发现有问题的sql。即是格式化好的输出文件中

  有关sql性能信息数据的数据库表的形式。

  我一般不用该参数

  RECORD

  创建一个包含客户端程序发出的所有的sql语句的脚本文件。

  注意,并不包含recursive SQL 。想知道它的用处吗?

  对了可以窥探别人程序是如何访问数据库的,从而对了解程序的访问流程。

  此时,最好不用sort参数,这样就可以按先后发出的顺序的到sql.

  例子1:

  将跟踪文件"dsdb2_ora_18468.trc"进行分析,并将其格式的结果放到"dsdb2_trace.out"文件中:

  TKPROF dsdb2_ora_18468.trc dsdb2_trace.out SYS=NO EXPLAIN=SCOTT/TIGER

  上面的例子中:

  EXPLAIN参数让TKPROF程序连接到SCOTT用户,然后用EXPLAIN PLAN命令给跟踪文件中的sql语句产生执行计划。SYS参数的值为NO,这样TKPROF就会忽略该跟踪文件中的recursive SQL。

  例子2:

  TKPROF DLSUN12_JANE_FG_SVRMGR_007.TRC OUTPUTA.PRF

  EXPLAIN=SCOTT/TIGER TABLE=SCOTT.TEMP_PLAN_TABLE_A

  INSERT=STOREA.SQL SYS=NO SORT=(EXECPU,FCHCPU)

  注意上面的所有命名应该都在一行中,否则需要有换行符。

  上面的例子中:

  TABLE参数使TKPROF使用scott用户下的TEMP_PLAN_TABLE_A表作为临时存放sql执行计划的表。

  INSERT参数使TKPROF产生一个名为STOREA.SQL的脚本文件,存放所有被跟踪的sql语句的统计数据。

  SORT参数使TKPROF先按照sql语句使用的cpu执行时间与该语句fetch操作使用的cpu时间进行排序,然后将其写到输出文件中。

  解释tkprof程序产生的格式化文件

  tkprof的格式化输出文件主要包含两大部分:

  header

  body

  summary

  header:

  主要包括一些描述信息,如TKPROF的版本、运行时间,各个统计项的描述。如:

  TKPROF: Release 8.1.7.0.0 - Production on 星期四 6月 30 13:10:59 2005

  (c) Copyright 2000 Oracle Corporation. All rights reserved.

  Trace file: D:\oracle\admin\xyj\udump\ORA01720.TRC

  Sort options: default

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

  count = number of times OCI procedure was executed

  cpu = cpu time in seconds executing

  elapsed = elapsed time in seconds executing

  disk = number of physical reads of buffers from disk

  query = number of buffers gotten for consistent read

  current = number of buffers gotten in current mode (usually for update)

  rows = number of rows processed by the fetch or execute call

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

  body:

  是我们主要关心的地方,有我们感兴趣的信息。如sql语句、sql语句的统计信息、sql语句的执行计划等。如

  select *

  from

  emp

  call count cpu elapsed disk query current rows

  ------- ----- ----- ------- ----- ------ -------- -----

  Parse 3 0.00 0.00 1 0 1 0

  Execute 3 0.00 0.00 0 0 0 0

  Fetch 6 0.00 0.00 1 6 12 36 61

  ------- ----- ----- ------- ----- ------ -------- -----

  total 12 0.00 0.00 2 6 13 36

  Misses in library cache during parse: 1

  Optimizer goal: CHOOSE

  Parsing user id: 19 (SCOTT)

  Rows Row Source Operation

  ------- ---------------------------------------------------

  12 TABLE ACCESS FULL EMP

  Rows Execution Plan

  ------- ---------------------------------------------------

  0 SELECT STATEMENT GOAL: CHOOSE

  12 TABLE ACCESS (FULL) OF 'EMP'

  DELETE FROM RM$HASH_ELMS

  call count cpu elapsed disk query current rows

  ------- ------ ------ -------- ----- ------ -------- ----

  Parse 2 0.00 0.00 0 0 0 0

  Execute 29 12.04 12.61 6786 6853 108 19

  Fetch 0 0.00 0.00 0 0 0 0

  ------- ------ ------ -------- ----- ------ -------- ----

  total 31 12.04 12.61 6786 6853 108 19

  Misses in library cache during parse: 0

  Optimizer hint: CHOOSE

  Parsing user id: 9 (DES12A) (recursive depth: 3)

  Rows Execution Plan

  ------- ---------------------------------------------------

  0 DELETE STATEMENT HINT: CHOOSE

  16 TABLE ACCESS (FULL) OF 'RM$HASH_ELMS'

  summary:

  对所有的sql语句各个执行阶段的统计数据的汇总:

  OVERALL TOTALS FOR ALL NON-RECURSIVE STATEMENTS - - 标明是用户发出的sql语句的统计数据的汇总

  call count cpu elapsed disk query current rows

  ------- ------ -------- -------- ----- ------ -------- -----

  Parse 7 0.00 0.00 2 201 2 0

  62

  Execute 7 0.00 0.00 1 2 7 1

  Fetch 10 0.00 0.00 2 67 16 52

  ------- ------ -------- -------- ----- ------ -------- -----

  total 24 0.00 0.00 5 270 25 53

  Misses in library cache during parse: 5

  OVERALL TOTALS FOR ALL RECURSIVE STATEMENTS - - 标明是系统发出的sql语句的统计数据的汇总

  call count cpu elapsed disk query current rows

  ------- ------ ------ -------- ----- ------ -------- ----

  Parse 75 0.00 0.00 2 3 2 0

  Execute 81 0.00 0.00 1 1 5 1

  Fetch 153 0.00 0.00 21 355 0 110

  ------- ------ ------ -------- ----- ------ -------- ----

  total 309 0.00 0.00 24 359 7 111

  Misses in library cache during parse: 29

  8 user SQL statements in session.

  74 internal SQL statements in session.

  82 SQL statements in session.

  5 statements EXPLAINed in this session.

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

  Trace file: D:\oracle\admin\xyj\udump\ORA01720.TRC

  Trace file compatibility: 8.00.04

  Sort options: default

  1 session in tracefile.

  8 user SQL statements in trace file.

  74 internal SQL statements in trace file.

  82 SQL statements in trace file.

  34 unique SQL statements in trace file.

  5 SQL statements EXPLAINed using schema:

  SCOTT.prof$plan_table

  Default table was used.

  Table was created.

  Table was dropped.

  825 lines in trace file.

  解释tkprof程序产生的格式化文件

  tkprof的格式化输出文件主要包含两大部分:

  header

  body

  summary

  header:

  主要包括一些描述信息,如TKPROF的版本、运行时间,各个统计项的描述。如:

  TKPROF: Release 8.1.7.0.0 - Production on 星期四 6月 30 13:10:59 2005

  (c) Copyright 2000 Oracle Corporation. All rights reserved.

  Trace file: D:\oracle\admin\xyj\udump\ORA01720.TRC

  Sort options: default

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

  count = number of times OCI procedure was executed

  cpu = cpu time in seconds executing

  elapsed = elapsed time in seconds executing

  disk = number of physical reads of buffers from disk

  query = number of buffers gotten for consistent read

  current = number of buffers gotten in current mode (usually for update)

  rows = number of rows processed by the fetch or execute call

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

  body:

  是我们主要关心的地方,有我们感兴趣的信息。如sql语句、sql语句的统计信息、sql语句的执行计划等。如

  select *

  from

  emp

  call count cpu elapsed disk query current rows

  ------- ----- ----- ------- ----- ------ -------- -----

  Parse 3 0.00 0.00 1 0 1 0

  Execute 3 0.00 0.00 0 0 0 0

  Fetch 6 0.00 0.00 1 6 12 36 61

  ------- ----- ----- ------- ----- ------ -------- -----

  total 12 0.00 0.00 2 6 13 36

  Misses in library cache during parse: 1

  Optimizer goal: CHOOSE

  Parsing user id: 19 (SCOTT)

  Rows Row Source Operation

  ------- ---------------------------------------------------

  12 TABLE ACCESS FULL EMP

  Rows Execution Plan

  ------- ---------------------------------------------------

  0 SELECT STATEMENT GOAL: CHOOSE

  12 TABLE ACCESS (FULL) OF 'EMP'

  DELETE FROM RM$HASH_ELMS

  call count cpu elapsed disk query current rows

  ------- ------ ------ -------- ----- ------ -------- ----

  Parse 2 0.00 0.00 0 0 0 0

  Execute 29 12.04 12.61 6786 6853 108 19

  Fetch 0 0.00 0.00 0 0 0 0

  ------- ------ ------ -------- ----- ------ -------- ----

  total 31 12.04 12.61 6786 6853 108 19

  Misses in library cache during parse: 0

  Optimizer hint: CHOOSE

  Parsing user id: 9 (DES12A) (recursive depth: 3)

  Rows Execution Plan

  ------- ---------------------------------------------------

  0 DELETE STATEMENT HINT: CHOOSE

  16 TABLE ACCESS (FULL) OF 'RM$HASH_ELMS'

  summary:

  对所有的sql语句各个执行阶段的统计数据的汇总:

  OVERALL TOTALS FOR ALL NON-RECURSIVE STATEMENTS - - 标明是用户发出的sql语句的统计数据的汇总

  call count cpu elapsed disk query current rows

  ------- ------ -------- -------- ----- ------ -------- -----

  Parse 7 0.00 0.00 2 201 2 0

  62

  Execute 7 0.00 0.00 1 2 7 1

  Fetch 10 0.00 0.00 2 67 16 52

  ------- ------ -------- -------- ----- ------ -------- -----

  total 24 0.00 0.00 5 270 25 53

  Misses in library cache during parse: 5

  OVERALL TOTALS FOR ALL RECURSIVE STATEMENTS - - 标明是系统发出的sql语句的统计数据的汇总

  call count cpu elapsed disk query current rows

  ------- ------ ------ -------- ----- ------ -------- ----

  Parse 75 0.00 0.00 2 3 2 0

  Execute 81 0.00 0.00 1 1 5 1

  Fetch 153 0.00 0.00 21 355 0 110

  ------- ------ ------ -------- ----- ------ -------- ----

  total 309 0.00 0.00 24 359 7 111

  Misses in library cache during parse: 29

  8 user SQL statements in session.

  74 internal SQL statements in session.

  82 SQL statements in session.

  5 statements EXPLAINed in this session.

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

  Trace file: D:\oracle\admin\xyj\udump\ORA01720.TRC

  Trace file compatibility: 8.00.04

  Sort options: default

  1 session in tracefile.

  8 user SQL statements in trace file.

  74 internal SQL statements in trace file.

  82 SQL statements in trace file.

  34 unique SQL statements in trace file.

  5 SQL statements EXPLAINed using schema:

  SCOTT.prof$plan_table

  Default table was used.

  Table was created.

  Table was dropped.

  825 lines in trace file.

  现只对body中的每部分作出详细说明

  1) sql语句的统计信息

  我们把select语句的执行过程分成3个阶段(分析?? 执行?? 取结果),把DML语句的执行分成2个阶段(分析?? 执行)。格式化的输出文件中列出了sql语句执行的每一阶段所耗费资源统计信息,对于这些信息,是以行列的模式给出的。每一行代表每个sql执行中的具体某一阶段所耗费的相应资源。这样通过分析每个阶段耗费的资源,可以知道哪个sql有问题,并进一步知道该sql执行过程中哪一阶段出现了问题,从而快速定位问题,进而迅速解决问题。

  下面对每个列进行说明:

  call : 表示sql语句执行的每个阶段,每个sql语句的活动被分成以下3部分:

  Parse: 语句被解析的次数,主要是生成执行计划。包含hard parse与soft parse。

  需要做的工作:权限检查,表、列、引用的表的存在性检查;

  比较执行计划,选出最好的一个等等。

  Execute: 真正执行语句的时间,对于DML语句,在此阶段中修改数据;

  对于select语句,这步只是标识出查询出的行。

  Fetch : 只对select语句有效,DML语句的执行并没有该阶段

  其它列的值都是与sql执行三个阶段中所耗费的资源的统计值

  COUNT

  一个语句被parsed、executed、fetched的次数

  CPU

  执行这个语句的每个阶段耗费的cpu时间

  ELAPSED

  执行这个语句的每个阶段耗费的总时间(包括上面的cpu时间与其它时间,如读数据)

  DISK

  每个阶段读硬盘的次数(有可能从file system buffer中取得数据)

  对于该参数,我们希望该值越小越好,如果该值很大,该sql需要调整,

  建立相关索引或看是否正确的使用了索引

  QUERY

  每个阶段以consistent mode 方式从数据库buffer中查询的buffers数。

  对于查询,其buffer一般都是以consistent mode模式被读取

  CURRENT

  每个阶段以current mode方式从数据库buffer中查询的buffers数。Buffers are often

  对于DML语句,需要的buffer是以current mode模式被读取的。

  QUERY + CURRENT 的和是该sql语句总的存取的buffer数目

  ROWS

  这个sql语句最后处理的行数,不包括子查询中查询出来的行数。

  对于select语句,该值产生于fetch阶段;对于dml该值产生于execute阶段。

  因为统计耗费的时间时,最小的计量单位为0.01秒,所以如果得到一个阶段中耗费的时间为0,并不表示这个阶段没有耗费时间,而是极可能说明这个阶段耗费的时间小于0.01秒,小于计量单位,数据库无法计时,只要以0.00表示

  2) 与执行计划有关的内容

  Misses in library cache during parse: 1 -- 说明hard parse的次数

  Optimizer goal: CHOOSE -- 采用的优化器

  Parsing user id: 19 (SCOTT) -- 那个用户执行的该sql

  Rows Row Source Operation

  ------- ---------------------------------------------------

  12 TABLE ACCESS FULL EMP

  Rows Execution Plan -- 下面是真正的执行计划

  ------- ---------------------------------------------------

  0 SELECT STATEMENT GOAL: CHOOSE

  12 TABLE ACCESS (FULL) OF 'EMP'

  Misses in library cache during parse: 这个统计值是一个比较重要的指标,如果该值与该语句的parse统计值基本相等,并且该值比较大,而且该sql的parse阶段耗费的资源比较多,则说明你的语句应该采用bind variable模式。

  执行计划部分也比较重要,它能看出查询是否用了索引,和各种关联操作所采用的方法。

  建议用autotrace进行跟踪而不是用上面方法进行跟踪的原因:

  虽然上面的方法给出了sql语句的执行计划、统计数据等信息,但是因为tkprof的格式化输出不给出详尽的costs与statistics信息,这使我们在利用格式化输出判断系统到底是使用基于代价的优化方法还是基于规则的优化方法时,感到很迷茫,我们不能明确的知道到底系统在使用那种优化器。

  但是仅仅因为这个原因就使我们放弃上面跟踪方法而该用autotrace功能是不够的,因为:

  1) 到底系统是使用基于规则的还是基于代价的优化器我们可以借助与其它信息来识别,而且从10G以后,oracle就宣布不再使用基于规则的优化器了。

  2) 上面的跟踪办法能实现autotrace不能完成的功能,因为autotrace只能跟踪本会话,而不能跟踪其它会话,这样假如有一个正在运行的程序出现了性能问题,我们就不能使用autrace去跟踪sql的执行情况。

  下面以一个具体的例子的数据说明如何利用格式化的输出文件进行sql的调整:

  第一步: - 现看格式化输出文件最后部分,即汇总部分

  ===========================================================

  OVERALL TOTALS FOR ALL NON-RECURSIVE STATEMENTS

  | call | count | cpu | elapsed | disk | query | current | rows |

  |--------- |------- |------ |--------- |--------- |-------- |--------- |-------- |

  | Parse | [A] 7 | 1.87 | 4.53 | 385 | [G] 553 | 22 | 0 |

  | Execute | [E] 7 | 0.03 | 0.11 | [P] 0 | [C] 0 | [D] 0 | [F] 0 |

  | Fetch | [E] 6 | 1.39 | 4.21 | [P] 182 | [C] 820 | [D] 3 | [F] 20 |

  --------------------------------------------------------------------------

  Misses in library cache during parse: 5

  Misses in library cache during execute: 1

  8 user SQL statements in session.

  12 internal SQL statements in session.

  [B] 54 SQL statements in session.

  3 statements EXPLAINed in this session.

  (1). 通过比较 [A] 与 [B],我们可以发现是否有过量的parsing现象。在上面的例子中,我们可以看到在session中执行了54个语句,但是只有7次parses,所以这是比较正常的,没有过量的parse现象。

  (2). 利用 [P], [C] & [D] 来决定数据库高速缓存的命中率问题

  Hit Ratio is logical reads/physical reads:

  Logical Reads = Consistent Gets + DB Block Gets

  Logical Reads = query + current

  Logical Reads = Sum[C] + Sum[D]

  Logical Reads = 0+820 + 0+3

  Logical Reads = 820 + 3

  Logical Reads = 823

  Hit Ratio = 1 - (Physical Reads / Logical Reads)

  Hit Ratio = 1 - (Sum[P] / Logical Reads)

  Hit Ratio = 1 - (128 / 823)

  Hit Ratio = 1 - (0.16)

  Hit Ratio = 0.84 or 84%

  (3). 我们希望fetch的次数要比rows小,即一次fetch可以取多行数据(array fetching),

  可以我们可以更高效的取得查询数据。

  这可以通过比较[E]与[F].

  [E] = 6 = Number of Fetches

  [F] = 20 = Number of Rows

  从上面的信息中我们可以看到,6次fetch总共取了20行数据,结果不是很坏。如果使用了经过良好配置arrayfetching,则可以用更少的fetch次数取到同样数量的数据,性能会更好。

  (4). [G] 表示为了对语句进行分析,读数据字典告诉缓存的次数

  - 这个参数对性能的影响不大,一般不用关心。而且这个统计值一般不是我们可以控制的。

  第二步 – 检查耗费大量资源的语句

  ===============================================

  update ...

  where ...

  | call | count | cpu | elapsed | disk | query | current | rows |

  |--------- |------- |----- |--------- |------ |-------- |--------- |-------- |

  | Parse | 1 | 7 | 122 | 0 | 0 | 0 | 0 |

  | Execute | 1 | 75 | 461 | 5 | [H] 297 | [I] 3 | [J] 1 |

  | Fetch | 0 | 0 | 0 | 0 | 0 | 0 | 0 |

  -----------------------------------------------------------------------

  [H] 表明需要访问297个数据块才能找到我们需要修改的数据。

  [I] 表明我们的修改操作才修改3个数据块中的数据

  [J] 表明我们只修改了一行数据(其它数据块的修改应为undo、redo信息)

  为了修改一行数据而要搜寻297个数据块。

  考虑是否需要在查许的列上建一个索引!

  第三步 – 查看是否有过量的parse现象

  ==============================

  select ...

  | call | count | cpu | elapsed | disk | query | current | rows |

  |--------- |------ -|--------- |--------- |----- -|------- -|-------- -|------ -|

  | Parse | [M] 2 | [N] 221 | 329 | 0 | 45 | 0 | 0 |

  | Execute | [O] 3 | [P] 9 | 17 | 0 | 0 | 0 | 0 |

  | Fetch | 3 | 6 | 8 | 0 | [L] 4 | 0 | [K] 1 |

  -------------------------------------------------------------------------

  Misses in library cache during parse: 2 [Q]

  [K] 表明这个查询只返回一行数据

  [L] 表明我们需要fetch 4次才能得到数据,这是正常的,因为需要额外的fetch操作以便检查是否fetch到cursor的最后,当然还可能有其它fetch开销。

  [M] 表明我们进行了两次parse(包含hard parse 与soft parse) – 这是我们不想看到的,

  特别是当parse阶段操作耗费cpu资源比execute阶段耗费的cpu资源([O] & [P])

  多得多得时候。 [Q] 表明这两个parse操作都是hard parse。如果[Q]的值为1,

  这这个语句有一个hard parse,然后跟着一个soft parse(仅仅从库缓存中得到

  上次分析的信息,比hard parse要高效的多)。

  对上面的例子来说,结果并不是特别的坏,因为该语句只执行2次,然而如果对于频繁执行的sql来说,如果几乎每次执行都需要hard parse,则结果就会变的很坏,此时我们说该语句有过量的parse现象(excessive parsing)。

  o解决该问题的方法:

  - 使用bind variables

  - 使shared pool足够大,从而在内存中容纳你执行过的每一条语句,

  以便下一次可以重用该语句。但这种方法治标不治本,在繁忙的系统中有时会引起

  ora-04031: unable to allocate %s bytes of shared memory (%s,%s,%s)

  错误。

  - 使用8i新引入的参数cursor_sharing,建议在经过测试后再使用该参数,因为有时

  使用该参数后会引起系统性能下降

  如何降低parse阶段使用的cpu时间

  1.Rewrite the application so statements do not continually reparse.

  2.Reduce parsing by using the initialization parameter SESSION_CACHED_CURSORS

  3.Use bind variables to reduce parsing.

  注意:

  记住如果cursor没有被关闭,将在tkprof的输出文件中看不到任何该sql的输出。设置 SQL_TRACE = false并不能关闭PL/SQL 的child cursors,所以要在自己的存储过程中养成及时关闭显式cursor的习惯。令我们高兴的是,在SQL*Plus中,语句一旦执行完毕,该语句对应的cursor也自动关闭了。

0
相关文章