你好,游客 登录 注册 搜索
背景:
阅读新闻

乱用Oracle Hint造成性能问题案例二

[日期:2015-02-03] 来源:Linux社区  作者:eric0435 [字体: ]

某医保系统在业务高峰期间相关模块使用不了查询不出结果,生成业务高峰期间上午10点到11点期间的AWR报告

从上面的top sql部分可以看到执行时间最长的已经达到19019秒,还有几个运行时间也要执行几千秒,其中SQLID为d7bv3q1camq5x的SQL逻辑读和物理读都在几千万甚至上亿次。而每次也就返回200多行记录。

其中SQL语句为:
select /*+ rule */
 a.stat_type,
 a.his_item_code,
 a.his_item_name,
 a.item_code,
 max((select count(*)
      from mt_fee_fin aa
      where a.hospital_id = aa.hospital_id
        and a.serial_no = aa.serial_no
        and a.item_code = aa.item_code)) as item_sn,
 a.item_name,
 a.medi_item_type,
 a.price,
 sum(a.dosage) as dosage,
 a.model,
 replace(a.standard, '  ', '') as standard,
 sum(a.money) as money,
 sum(nvl(d.audit_money, 0)) as audit_money,
 d.hosp_reason_staff as hosp_reason_staff,
 d.hosp_reason_date as hosp_reason_date,
 d.hosp_reason_staffid as hosp_reason_staffid,
 d.hosp_reason as hosp_reason,
 d.center_resualt as center_resualt,
 d.center_flag as center_flag,
 d.audit_reason_id as audit_reason_id,
 sum(nvl(b.all_cash, 0)) as all_cash,
 (case
  when a.medi_item_type = '0' then
    (SELECT bo_flag
      FROM bs_item
      WHERE bs_item.item_code = a.item_code
        AND ROWNUM < 2)
  else
    (SELECT bo_flag
      FROM bs_medi
      WHERE bs_medi.medi_code = a.item_code
        AND ROWNUM < 2)
 end) as bo_flag,
 sum(nvl(b.part_cash, 0)) as part_cash,
 decode(nvl(d.audit_reason_id, 0),
        0,
        d.audit_reason,
        '%%' || to_char(d.audit_reason_id) || '%%') as audit_reason
  from mt_fee_fin a,
      pm_account_biz c,
      pm_fee_audit d,
      (select hospital_id,
              serial_no,
              policy_item_code,
              serial_fee,
              fee_batch,
              SUM(decode(fund_id,
                          '999',
                          decode(b.label_flag, '101', real_pay, 0),
                          '003',
                          decode(label_flag, '101', real_pay, 0),
                          0)) AS all_cash,
              SUM(decode(fund_id,
                          '999',
                          decode(b.label_flag, '102', real_pay, 0),
                          '003',
                          decode(label_flag, '102', real_pay, 0),
                          0)) AS part_cash
          from mt_pay_record_fin b
        where b.hospital_id = '4307210003'
          and b.serial_no = '25735455'
          and serial_fee <> 0
          and valid_flag = '1'
        group by hospital_id,
                  serial_no,
                  policy_item_code,
                  serial_fee,
                  fee_batch) b
 where a.hospital_id = c.hospital_id
  and a.serial_no = c.serial_no
  and a.hospital_id = '4307210003'
  and a.serial_no = '25735455'
  and a.hospital_id = b.hospital_id(+)
  and a.serial_fee = b.serial_fee(+)
  and a.serial_no = b.serial_no(+)
  and a.fee_batch = b.fee_batch(+)
  and a.valid_flag = '1'
  and c.valid_flag = '1'
  and d.audit_staff_id(+) = 2103
  and d.AUDIT_PHASE(+) = '1'
  and d.serial_fee(+) <> 0
  and a.serial_fee = d.serial_fee(+)
  and d.account_id(+) = 16905170
  and c.account_id = 16905170
 group by a.stat_type,
          a.item_name,
          a.his_item_name,
          a.price,
          a.his_item_code,
          a.item_code,
          a.medi_item_type,
          a.model,
          a.standard,
          d.hosp_reason,
          d.center_resualt,
          d.center_flag,
          d.hosp_reason_staff,
          d.hosp_reason_date,
          d.hosp_reason_staffid,
          d.audit_reason_id,
          d.audit_reason
 Order By a.stat_type, a.item_name, a.his_item_name

 

在程序的SQL语句使用了/*+ rule */提示,就会让优化器使用RBO,不使用CBO。 这样使得优化器少了许多选择CBO高效执行计划的机会。当我们去掉/*+ rule */,使用CBO时其执行结果如下:
SQL> select
  2  a.stat_type,
  3  a.his_item_code,
  4  a.his_item_name,
  5  a.item_code,
  6  max((select count(*)
  7        from mt_fee_fin aa
  8        where a.hospital_id = aa.hospital_id
  9          and a.serial_no = aa.serial_no
 10          and a.item_code = aa.item_code)) as item_sn,
 11  a.item_name,
 12  a.medi_item_type,
 13  a.price,
 14  sum(a.dosage) as dosage,
 15  a.model,
 16  replace(a.standard, '  ', '') as standard,
 17  sum(a.money) as money,
 18  sum(nvl(d.audit_money, 0)) as audit_money,
 19  d.hosp_reason_staff as hosp_reason_staff,
 20  d.hosp_reason_date as hosp_reason_date,
 21  d.hosp_reason_staffid as hosp_reason_staffid,
 22  d.hosp_reason as hosp_reason,
 23  d.center_resualt as center_resualt,
 24  d.center_flag as center_flag,
 25  d.audit_reason_id as audit_reason_id,
 26  sum(nvl(b.all_cash, 0)) as all_cash,
 27  (case
 28    when a.medi_item_type = '0' then
 29      (SELECT bo_flag
 30        FROM bs_item
 31        WHERE bs_item.item_code = a.item_code
 32          AND ROWNUM < 2)
 33    else
 34      (SELECT bo_flag
 35        FROM bs_medi
 36        WHERE bs_medi.medi_code = a.item_code
 37          AND ROWNUM < 2)
 38  end) as bo_flag,
 39  sum(nvl(b.part_cash, 0)) as part_cash,
 40  decode(nvl(d.audit_reason_id, 0),
 41          0,
 42          d.audit_reason,
 43          '%%' || to_char(d.audit_reason_id) || '%%') as audit_reason
 44    from mt_fee_fin a,
 45        pm_account_biz c,
 46        pm_fee_audit d,
 47        (select hospital_id,
 48                serial_no,
 49                policy_item_code,
 50                serial_fee,
 51                fee_batch,
 52                SUM(decode(fund_id,
 53                            '999',
 54                            decode(b.label_flag, '101', real_pay, 0),
 55                            '003',
 56                            decode(label_flag, '101', real_pay, 0),
 57                            0)) AS all_cash,
 58                SUM(decode(fund_id,
 59                            '999',
 60                            decode(b.label_flag, '102', real_pay, 0),
 61                            '003',
 62                            decode(label_flag, '102', real_pay, 0),
 63                            0)) AS part_cash
 64            from mt_pay_record_fin b
 65          where b.hospital_id = '4307210003'
 66            and b.serial_no = '25735455'
 67            and serial_fee <> 0
 68            and valid_flag = '1'
 69          group by hospital_id,
 70                    serial_no,
 71                    policy_item_code,
 72                    serial_fee,
 73                    fee_batch) b
 74  where a.hospital_id = c.hospital_id
 75    and a.serial_no = c.serial_no
 76    and a.hospital_id = '4307210003'
 77    and a.serial_no = '25735455'
 78    and a.hospital_id = b.hospital_id(+)
 79    and a.serial_fee = b.serial_fee(+)
 80    and a.serial_no = b.serial_no(+)
 81    and a.fee_batch = b.fee_batch(+)
 82    and a.valid_flag = '1'
 83    and c.valid_flag = '1'
 84    and d.audit_staff_id(+) = 2103
 85    and d.AUDIT_PHASE(+) = '1'
 86    and d.serial_fee(+) <> 0
 87    and a.serial_fee = d.serial_fee(+)
 88    and d.account_id(+) = 16905170
 89    and c.account_id = 16905170
 90  group by a.stat_type,
 91            a.item_name,
 92            a.his_item_name,
 93            a.price,
 94            a.his_item_code,
 95            a.item_code,
 96            a.medi_item_type,
 97            a.model,
 98            a.standard,
 99            d.hosp_reason,
100            d.center_resualt,
101            d.center_flag,
102            d.hosp_reason_staff,
103            d.hosp_reason_date,
104            d.hosp_reason_staffid,
105            d.audit_reason_id,
106            d.audit_reason
107  Order By a.stat_type, a.item_name, a.his_item_name
108  ;
....省略...

277 rows selected.

Elapsed: 00:00:00.05

Execution Plan
----------------------------------------------------------
Plan hash value: 1363767461

-----------------------------------------------------------------------------------------------------------
| Id  | Operation                      | Name                    | Rows  | Bytes | Cost (%CPU)| Time    |
-----------------------------------------------------------------------------------------------------------
|  0 | SELECT STATEMENT                |                        |    1 |  338 |    7  (43)| 00:00:01 |
|  1 |  SORT AGGREGATE                |                        |    1 |    36 |            |          |
|*  2 |  TABLE ACCESS BY INDEX ROWID  | MT_FEE_FIN              |    1 |    36 |    1  (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN            | PK_MT_FEE_FIN          |    1 |      |    1  (0)| 00:00:01 |
|*  4 |  COUNT STOPKEY                  |                        |      |      |            |          |
|  5 |  TABLE ACCESS BY INDEX ROWID  | BS_ITEM                |    12 |  192 |    3  (0)| 00:00:01 |
|*  6 |    INDEX RANGE SCAN            | IDX_ITEM_CODE          |    12 |      |    1  (0)| 00:00:01 |
|*  7 |  COUNT STOPKEY                |                        |      |      |            |          |
|  8 |    TABLE ACCESS BY INDEX ROWID  | BS_MEDI                |    12 |  216 |    3  (0)| 00:00:01 |
|*  9 |    INDEX RANGE SCAN            | IDX_MEDI_CODE          |    12 |      |    1  (0)| 00:00:01 |
|  10 |  SORT GROUP BY                  |                        |    1 |  338 |    7  (43)| 00:00:01 |
|* 11 |  HASH JOIN OUTER              |                        |    1 |  338 |    6  (34)| 00:00:01 |
|  12 |    NESTED LOOPS OUTER          |                        |    1 |  283 |    3  (0)| 00:00:01 |
|  13 |    NESTED LOOPS                |                        |    1 |  139 |    2  (0)| 00:00:01 |
|* 14 |      TABLE ACCESS BY INDEX ROWID| PM_ACCOUNT_BIZ          |    1 |    29 |    1  (0)| 00:00:01 |
|* 15 |      INDEX UNIQUE SCAN        | PK_PM_ACCOUNT_BIZ      |    1 |      |    1  (0)| 00:00:01 |
|* 16 |      TABLE ACCESS BY INDEX ROWID| MT_FEE_FIN              |    1 |  110 |    1  (0)| 00:00:01 |
|* 17 |      INDEX RANGE SCAN          | PK_MT_FEE_FIN          |    1 |      |    1  (0)| 00:00:01 |
|  18 |    TABLE ACCESS BY INDEX ROWID | PM_FEE_AUDIT            |    1 |  144 |    1  (0)| 00:00:01 |
|* 19 |      INDEX RANGE SCAN          | PK_PM_FEE_AUDIT        |    1 |      |    1  (0)| 00:00:01 |
|  20 |    VIEW                        |                        |    1 |    55 |    2  (50)| 00:00:01 |
|  21 |    HASH GROUP BY              |                        |    1 |    57 |    2  (50)| 00:00:01 |
|* 22 |      TABLE ACCESS BY INDEX ROWID| MT_PAY_RECORD_FIN      |    1 |    57 |    1  (0)| 00:00:01 |
|* 23 |      INDEX RANGE SCAN          | IDX_MT_PAY_RECORD_FIN_2 |    1 |      |    1  (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

  2 - filter("AA"."ITEM_CODE"=:B1)
  3 - access("AA"."HOSPITAL_ID"=:B1 AND "AA"."SERIAL_NO"=:B2)
  4 - filter(ROWNUM<2)
  6 - access("BS_ITEM"."ITEM_CODE"=:B1)
  7 - filter(ROWNUM<2)
  9 - access("BS_MEDI"."MEDI_CODE"=:B1)
  11 - access("A"."HOSPITAL_ID"="B"."HOSPITAL_ID"(+) AND "A"."SERIAL_FEE"="B"."SERIAL_FEE"(+) AND
              "A"."SERIAL_NO"="B"."SERIAL_NO"(+) AND "A"."FEE_BATCH"="B"."FEE_BATCH"(+))
  14 - filter("C"."SERIAL_NO"='25735455' AND "C"."HOSPITAL_ID"='4307210003' AND
              "C"."VALID_FLAG"='1')
  15 - access("C"."ACCOUNT_ID"=16905170)
  16 - filter("A"."VALID_FLAG"='1')
  17 - access("A"."HOSPITAL_ID"='4307210003' AND "A"."SERIAL_NO"='25735455')
  19 - access("D"."ACCOUNT_ID"(+)=16905170 AND "D"."AUDIT_PHASE"(+)='1' AND
              "A"."SERIAL_FEE"="D"."SERIAL_FEE"(+))
      filter(TO_NUMBER("D"."AUDIT_STAFF_ID"(+))=2103 AND "D"."SERIAL_FEE"(+)<>0 AND
              "D"."AUDIT_PHASE"(+)='1' AND "A"."SERIAL_FEE"="D"."SERIAL_FEE"(+))
  22 - filter("SERIAL_FEE"<>0 AND "VALID_FLAG"='1')
  23 - access("B"."HOSPITAL_ID"='4307210003' AND "B"."SERIAL_NO"='25735455')


Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
      1247  consistent gets
        12  physical reads
          0  redo size
      9160  bytes sent via SQL*Net to client
      2220  bytes received via SQL*Net from client
          8  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
        227  rows processed


其中逻辑读为1247,物理读12,比起几千万上亿次,提高了上万倍。

后面经检查发现在这套系统中,有大理SQL使用了/*+ rule */ Hint,最好的处理方法是修改SQL代码,这里为了应急我使用了_optimizer_ignore_hints参数让优化器忽略所有的hint。

更多Oracle相关信息见Oracle 专题页面 http://www.linuxidc.com/topicnews.aspx?tid=12

本文永久更新链接地址http://www.linuxidc.com/Linux/2015-02/112771.htm

linux
本文评论   查看全部评论 (0)
表情: 表情 姓名: 字数

       

评论声明
  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或删除其管辖留言中的任意内容
  • 本站有权在网站内转载或引用您的评论
  • 参与本评论即表明您已经阅读并接受上述条款