查询优化手段和gather_plan_statistics hint:
在10g以后我们可以通过利用gather_plan_statistics提示来了解更多的SQL执行统计信息,具体使用方法如下: 如果在statistics_level参数设置为ALL,或者执行的sql使用了gather_plan_statistics hint,则在sql执行后,会在v sqlplanstatisticsall视图中查到SQL的执行统计信息,例如逻辑读,物理读,基数等等。这些数据对于性能诊断有着非常大的帮助。同时v sql_plan中的执行计划,与通过EXPLAIN PLAN得到的执行计划相比,前者是oracle执行sql时真正使用的执行计划,而后者则可能不是真正的执行计划;同时有的时候,执行过的sql使用了绑定变量,而oracle在解析sql时通常会进行绑定变量窥探,这个时候我们不能使用EXPLAIN PLAN来得到那个sql的执行计划,就算得到的跟那个sql的真实的执行计划是不一样的,所以有时我们更愿意直接从v$sql_plan中得到执行计划。 下面结合tom的文章实例讲一下这个hint得用法:原文:http://www.oracle.com/technetwork/issue-archive/2014/14-nov/o64asktom-2298498.html ——————————————————————————— 一个查询调优的例子: SQL> create table t as select case when mod(rownum,200000) = 0 then 5 else mod(rownum,4) end X, rpad( ‘x’, 100, ‘x’ ) data from dual connect by level <= 1000000 / –此处为了迷惑优化器而制造了分布不均的数据(skew data) SQL> create index t_idx on t(x); Index created. SQL> exec dbms_stats.gather_table_stats( user, ‘T’ ); PL/SQL procedure successfully completed. SQL> select x, count(*) from t group by x order by x / X COUNT(*) ——————————— ——————————— 0 249995 1 250000 2 250000 3 250000 5 5 SQL> select /*+ gather_plan_statistics */ count(data) from t where x = 5; / –查看上面查询执行计划 SQL> select * from table( dbms_xplan.display_cursor( format=> ‘allstats last’ ) ) / PLAN_TABLE_OUTPUT —————————————————————————————————— SQL_ID cdwn5mqb0cpg1, child number 0 —————————————————————————————————— select /*+ gather_plan_statistics */ count(data) from t where x = 5 Plan hash value: 2966233522 ——————————————————————————————————————————————————————————————————————————— | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | ——————————————————————————————————————————————————————————————————————————— | 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.08 | | 1 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.08 | |* 2 | TABLE ACCESS FULL| T | 1 | 200K| 5 |00:00:00.08 | ——————————————————————————————————————————————————————————————————————————— Predicate Information (identified by operation id): ——————————————————————————————————————————————————————————————————————————— 2 - filter(“X”=5) 20 rows selected. –可以看到执行计划走了全表扫描并且E-Rows,即预估基数为200K而A-rows,即实际返回基数为5差距相当大(a huge difference)。原因就是优化器对X列上分布不均的数据没有察觉。 –此时,我们需要提供X列上的直方图(histogram)让优化器掌控全面的基数信息。 SQL> select histogram from user_tab_columns where table_name = ‘T’ and column_name = ‘X’; HISTOGRAM ————— NONE SQL> exec dbms_stats.gather_table_stats( user, ‘T’, no_invalidate=>false ); SQL> select histogram from user_tab_columns where table_name = ‘T’ and column_name = ‘X’; HISTOGRAM ————— FREQUENCY —-此处是用NO_INVALIDATE参数使之前的依赖游标失效,保证下次运行该查询执行硬解析(hard parse) –再次执行前面的查询操作,查看运行时计划信息 PLAN_TABLE_OUTPUT —————————————————————————————————— SQL_ID cdwn5mqb0cpg1, child number 0 —————————————————————————————————— select /*+ gather_plan_statistics */ count(data) from t where x = 5 Plan hash value: 1789076273 ———————————————————————————————————————————————————————————————————————— | Id | Operation | Name | Starts | E-Rows | A-Rows | ———————————————————————————————————————————————————————————————————————— | 0 | SELECT STATEMENT | | 1 | | 1 | | 1 | SORT AGGREGATE | | 1 | 1 | 1 | | 2 | TABLE ACCESS BY INDEX ROWID| T | 1 | 182 | 5 | |* 3 | INDEX RANGE SCAN | T_IDX | 1 | 182 | 5 | ———————————————————————————————————————————————————————————————————————— ————– | A-Time | ————– |00:00:00.01 | |00:00:00.01 | |00:00:00.01 | |00:00:00.01 | Predicate Information (identified by operation id): ———————————————————————————————————————————————————————————————————————— 3 - access(“X”=5) 21 rows selected. –此时执行计划走的是索引范围扫描且E-Rows已经很接近A-Rows,执行时间也大大缩减。 –这里你可能会犯嘀咕:咋第一次收集表信息时没有生成直方图? 看下tom的原话: This change is likely due to the default value of the METHOD_OPT parameter used with DBMS_STATS. In Oracle Database 10g, the METHOD_OPT parameter defaults to a SIZE AUTO. After you ran a query, the database remembered the predicates and updated a dictionary table, SYS.COL_USAGE .Then,thenexttimeyouranDBMSSTATStogatherstatisticsonthesetables,DBMSSTATSqueriedthattabletofindoutwhatcolumnsshouldhavehistogramscollectedautomatically,basedonpastqueryworkload.Itlookedatyourpredicatesandsaid,“Hmm,thesecolumnsarecandidatesforhistogramsbasedonthequeriestheendusershavebeenrunning.”听起来很神奇吧?事实却是如此!△补充1:关于管道函数dbmsxplan.displaycursor(format=>‘allstatslast′)要解释下几个参数的取值含义:—functiondisplaycursor(sqlidvarchar2defaultnull,—cursorchildnointegerdefault0,—formatvarchar2default‘TYPICAL′)——−sqlid:指定SQLID取值为V SQL.SQL_ID, V SESSION.SQLID,或者V SESSION.PREV_SQL_ID,如果不指定则默认为最后执行语句SQL_ID — - cursor_child_no: 指定sql游标的子号,取值为V SQL.CHILDNUMBER或者inV SESSION.SQL_CHILD_NUMBER,V$SESSION.PREV_CHILD_NUMBER, 此参数只有指定sql_id情况下才有效。如果不指定,则指定sql_id下所有子游标都显示。 — - format: — 指定输出列,值取自视图:v$sql_plan_statistics_all. — — IOSTATS: 假设基本计划统计在SQL执行时已收集(使用gather_plan_statistics提示或设置statistics_level参数为ALL) — 此格式将展示所有游标执行的(或仅最后执行游标)IO统计。 — MEMSTATS: — 假设PGA内存管理开启(例如:pga_aggregate_target参数设置为非0值),此格式允许展示内存管理统计 (例如:操作执行模式,内存使用,溢出到磁盘字节数) — ALLSTATS: 指定’IOSTATS MEMSTATS’的快捷命令 — — LAST: — 此格式就是指定仅显示最后执行sql的统计 — — Also, the following two formats are still supported for backward — compatibility: 另外以下2个参数为了向后兼容而保留 — — ‘RUNSTATS_TOT’: Same as ‘IOSTATS’, i.e. displays IO statistics — for all executions of the specified cursor. — ‘RUNSTATS_LAST’: Same as ‘IOSTATS LAST’, i.e. displays the runtime — statistics for the last execution of the cursor. — 权限要求: 必须具有:SELECT ON V SQLPLANSTATISTICSALL,V SQL, 和 V SQLPLAN权限。△补充2:还可以使用另一路径查询SQL运行时执行计划:HR@orcl>select/∗+gatherplanstatisticsempplan∗/count(1)fromemployees;COUNT(1)———−107HR@orcl>SELECTSQLID,CHILDNUMBERFROMV SQL WHERE SQL_TEXT LIKE '%emp_plan%' and SQL_TEXT NOT LIKE '%V SQLSQLIDCHILDNUMBER————−————2c5q2d8489ybt0HR@orcl>SELECT∗FROMTABLE(DBMSXPLAN.DISPLAYCURSOR(‘2c5q2d8489ybt′,0,′ALLIOSTATSLAST′));PLANTABLEOUTPUT———————————————————————————————————————————–SQLID2c5q2d8489ybt,childnumber0————————————−select/∗+gatherplanstatisticsempplan∗/count(1)fromemployeesPlanhashvalue:3580537945—————————————————————————————————————————−|Id|Operation|Name|Starts|E−Rows|Cost(—————————————————————————————————————————−|0|SELECTSTATEMENT||1||1(100)||1|00:00:00.02|1|1||1|SORTAGGREGATE||1|1|||1|00:00:00.02|1|1||2|INDEXFULLSCAN|EMPEMAILUK|1|107|1(0)|00:00:01|107|00:00:00.02|1|1|—————————————————————————————————————————−QueryBlockName/ObjectAlias(identifiedbyoperationid):————————————————————−1−SEL 1 2 - SEL 1/EMPLOYEES@SEL 1 Column Projection Information (identified by operation id): PLAN_TABLE_OUTPUT ———————————————————————————————————————————– ———————————————————– 1 - (#keys=0) COUNT(*)[22] 已选择25行。—————————————
Dylan Presents.