SQL性能的度量 - 通过v$sql_plan查询执行计划

在分析SQL语句性能时,视图v$sql_plan很有用,以下分几点举例说明。


通过以下脚本可以查询库缓存中已经运行过的SQL的执行计划

col "Explain Plan" for a100

select ' ' as "No.", '| Operation                         |Object Name                    |  Rows | Bytes|   Cost |'

as "Explain Plan" from dual

union all

select to_char(id, '999'), rpad('| ' || substr(lpad(' ', 1*(depth-1)) || operation ||

       decode(options, null, ' ' || substr(optimizer, 1, 7), ' ' || options), 1, 35), 36, ' ') || '|' ||

       rpad(decode(id, 0, '                            ',

       substr(decode(substr(object_name, 1, 7), 'SYS_LE_', null, object_name)

       || ' ', 1, 30)), 31, ' ') || '|' || lpad(decode(cardinality, null, '  ',

       decode(sign(cardinality - 1000), -1, cardinality || ' ',

       decode(sign(cardinality - 1000000), -1, trunc(cardinality / 1000) || 'K',

       decode(sign(cardinality - 1000000000), -1, trunc(cardinality / 1000000) || 'M',

       trunc(cardinality / 1000000000) || 'G')))), 7, ' ') || '|' ||

       lpad(decode(bytes, null, ' ',

       decode(sign(bytes - 1024), -1, bytes || ' ',

       decode(sign(bytes - 1048576), -1, trunc(bytes / 1024) || 'K',

       decode(sign(bytes - 1073741824), -1, trunc(bytes / 1048576) || 'M',

       trunc(bytes / 1073741824) || 'G')))), 6, ' ') || '|' ||

       lpad(decode(cost, null, ' ', decode(sign(cost - 10000000), -1, cost || ' ',

       decode(sign(cost - 1000000000), -1, trunc(cost / 1000000) || 'M',

       trunc(cost / 1000000000) || 'G'))), 8, ' ') || '|'

  from v$sql_plan sp

 where sp.sql_id = '&sql_id';


输入 sql_id 的值:  a08vqym1n4k5n


No.  Explain Plan

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

     | Operation                         |Object Name                    |  Rows | Bytes|   Cost |

   0 | SELECT STATEMENT ALL_ROW          |                               |       |      |      2 |

   1 | NESTED LOOPS                      |                               |     1 |  821 |      2 |

   2 |  NESTED LOOPS                     |                               |     1 |  808 |      2 |

   3 |   HASH JOIN                       |                               |     1 |  782 |      2 |

   4 |    NESTED LOOPS                   |                               |     1 |  736 |      1 |

   5 |     NESTED LOOPS                  |                               |     1 |  718 |      0 |

   6 |      FIXED TABLE FULL             |X$KSUSE                        |     1 |  182 |      0 |

   7 |      FIXED TABLE FIXED INDEX      |X$KGLCURSOR_CHILD_SQLID (ind:2 |     1 |  536 |      0 |

   8 |     INDEX RANGE SCAN              |I_AUDIT_ACTIONS                |     1 |   18 |      1 |

   9 |    FIXED TABLE FULL               |X$KSUPR                        |     1 |   46 |      0 |

  10 |   FIXED TABLE FIXED INDEX         |X$KSLWT (ind:1)                |     1 |   26 |      0 |

  11 |  FIXED TABLE FIXED INDEX          |X$KSLED (ind:2)                |     1 |   13 |      0 |


结合v$sqltext可以快速找到库缓存中执行全表扫描或全索引扫描的SQL语句

以下查找出全表扫描的SQL语句

select t.address,

       t.hash_value,

       t.sql_id,

       p.child_number,

       t.piece,

       sql_text,

       p.object_name

  from v$sqltext t, v$sql_plan p

 where t.hash_value = p.hash_value

   and p.operation = 'TABLE ACCESS'

   and p.options = 'FULL'

   and p.object_owner = 'RMES'

 order by t.address, t.hash_value, t.sql_id, p.child_number, t.piece;


ADDRESS          HASH_VALUE SQL_ID        CHILD_NUMBER      PIECE SQL_TEXT                                                         OBJECT_NAME

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

000007FF5567C198  580882336 cwmfwqcj9z3x0            0          0 DELETE RMES.R_WIP_AGV_T T WHERE T.LINE_ID = :B1 AND T.WIP_AGV_ID R_WIP_STATION_T

000007FF5567C198  580882336 cwmfwqcj9z3x0            0          1  NOT IN (SELECT T1.WIP_AGV_ID FROM RMES.R_WIP_STATION_T T1 WHERE R_WIP_STATION_T

000007FF5567C198  580882336 cwmfwqcj9z3x0            0          2  T1.WIP_AGV_ID IS NOT NULL AND T1.WIP_AGV_ID <> 0 AND T1.LINE_ID R_WIP_STATION_T

000007FF5567C198  580882336 cwmfwqcj9z3x0            0          3  = :B1 )                                                         R_WIP_STATION_T

000007FF5590F710 2960126118 dtsrgu6s6zw56            2          0 UPDATE RMES.R_WIP_STATION_T SET GROUP_ID = :B7 , STATION_ID = :B R_WIP_STATION_T

000007FF5590F710 2960126118 dtsrgu6s6zw56            2          1 6 , WIP_EMP_ID = :B5 , TEAM_ID = :B4 , SHIFT_ID = :B3 , ERROR_FL R_WIP_STATION_T

000007FF5590F710 2960126118 dtsrgu6s6zw56            2          2 AG = '0', IN_LINE_TIME = :B2 , OUT_LINE_TIME = :B2 , IN_STATION_ R_WIP_STATION_T

000007FF5590F710 2960126118 dtsrgu6s6zw56            2          3 TIME = SYSDATE, OUTLINE_FLAG = '1', HAD_OUTLINED = '1' WHERE WIP R_WIP_STATION_T

000007FF5590F710 2960126118 dtsrgu6s6zw56            2          4 _TRACKING_ID = :B1                                               R_WIP_STATION_T


以下查找出全索引扫描的SQL语句

select t.address,

       t.hash_value,

       t.sql_id,

       p.child_number,

       t.piece,

       sql_text,

       p.object_name

  from v$sqltext t, v$sql_plan p

 where t.hash_value = p.hash_value

   and p.operation = 'INDEX'

   and p.options = 'FULL SCAN'

   and p.object_owner = 'RMES'

 order by t.address, t.hash_value, t.sql_id, p.child_number, t.piece;


ADDRESS          HASH_VALUE SQL_ID        CHILD_NUMBER      PIECE SQL_TEXT                                                         OBJECT_NAME

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

000007FF55FC55E8 1305393110 b4ysx616wxdyq            0          0 select pd.force,pd.position from rmes.r_press_t p,rmes.r_press_d IDX_PK_PRESS_DETAIL_ID

000007FF55FC55E8 1305393110 b4ysx616wxdyq            0          1 etail_t pd where p.press_id=pd.press_id and p.press_id = -1 ORDE IDX_PK_PRESS_DETAIL_ID

000007FF55FC55E8 1305393110 b4ysx616wxdyq            0          2 R BY PD.PRESS_DETAIL_ID ASC                                      IDX_PK_PRESS_DETAIL_ID


以下通过等待事件查询执行计划

col operation for a50

select hash_value,

       child_number,

       lpad(' ', 2 * depth) || operation || ' ' || options ||

       decode(id, 0, substr(optimizer, 1, 6) || ' Cost = ' || to_char(cost)) operation,

       object_name,

       cost,

       round(bytes / 1024) kbytes

  from v$sql_plan

 where hash_value in

       (select sql_hash_value from v$session where event = '&waitevent')

 order by hash_value, child_number, id;


输入 waitevent 的值:  enq: TX - row lock contention


HASH_VALUE CHILD_NUMBER OPERATION                                          OBJECT_NAME                    COST       KBYTES

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

2186674966            0 SELECT STATEMENT ALL_RO Cost = 3                                                   3

2186674966            0   FOR UPDATE

2186674966            0     BUFFER SORT

2186674966            0       TABLE ACCESS FULL                            C_LINE_T                        3          1


而所有会话的当前等待事件可以通过v$session_wait查询

col username for a10

col event for a60

select sw.sid, s.serial#, s.username, sw.event, sw.wait_time, sw.state from v$session s, v$session_wait sw where sw.event not like 'rdbms%' and sw.sid = s.sid;


最后可以通过等待事件找到对应的SQL语句

select sql_text

  from v$sql

 where sql_id =

       (select sql_id

          from v$session

         where sid =

               (select sid from v$session_wait where event = '&waitwvent'));


输入 waitwvent 的值:  enq: TX - row lock contention


SQL_TEXT

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

select * from cmes.c_line_t for update

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

转载于:http://blog.itpub.net/28974745/viewspace-2150328/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
优化后的代码如下: ```bash #!/bin/bash set -euo pipefail # 加载函数脚本和常用变量 common_path=/path/to/common . "${common_path}/edw_function.sh" . "${common_path}/edw_setting.sh" # 解析参数 sql_file_name=$1 etl_dt=$2 start_offset=$(( $3 + 1 )) end_offset=$(( $4 + 1 )) table_name=${sql_file_name:11} download_sql_file=/dmcp/nas/edw/hql/gp_download/${sql_file_name}.sql exec_file=/dmcp/nas/edw/exec/${today_dt_1}/${sql_file_name}_exec.sql data_path=/data/gp_download/${table_name} # 检查下载 SQL 文件是否存在 if [ ! -e "${download_sql_file}" ]; then echo "${download_sql_file} not found" exit 99 fi # 生成执行 SQL 文件 if [ -e "${exec_file}" ]; then rm -f "${exec_file}" fi envsubst < "${download_sql_file}" > "${exec_file}" # 删除旧数据 rm -f "${data_path}/0*" # 执行 Hive SQL if hive -f "${exec_file}"; then curr_time=$(date +"%Y-%m-%d %H:%M:%S") echo "${curr_time} download ${table_name} succeeded" else curr_time=$(date +"%Y-%m-%d %H:%M:%S") echo "${curr_time} download ${table_name} failed" exit 99 fi ``` 优化点如下: 1. 添加 `set -euo pipefail`,使得脚本在发生错误时立即退出。 2. 使用双引号包裹变量,以避免由空格引起的解析错误。 3. 使用 `$(( ))` 表示算术表达式。 4. 修改变量名,使其更具有可读性。 5. 将日志输出提取出来,使代码更加清晰。 6. 删除了一些不必要的条件判断,如文件是否存在的判断已经在第 12 行执行了,没有必要在第 22 行重复判断。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值