用MERGE改写UPDATE的优化

 某系统负责人反应一个新上线的存储过程跑了 10个小时没出结果,中间卡在了一个update语句上面:

  ETL日志看到这个流程2017-04-11 23:59:43开始跑 ,到第二天10点还没有跑完

开发告诉我他们存储过程里面新上线了一条update语句,在测试环境测试可以跑出结果:sql如下

 UPDATE UUU_UIU_ITTTTTTTTT S
   SET S.ASSET_LOSS_PRVS =       
       (SELECT LOSS_BAL
          FROM ZZZ_Z_ZZZZ_ZZ SW
         WHERE SW.ZZZZ_NNN = S.MA_ZZZZ_NN
           AND ETL_DATE = :B1) *
       (SELECT F.CONVERSION_RATE
          FROM VW_DDIDD_DDDDD F
         WHERE CONVERSION_DATE = :B1
           AND F.CURRENCY_CD = S.CURRENCY_CD)           
 WHERE S.GL_ACCT_ID IS NOT NULL;

通过V$SESSION视图同样看到的是这个update语句active状态持续了很长时间,可以断定整个存储过程的主要问题是这个update语句

 

开始优化

1.查看这条SQL的执行计划和这个SQL相关表的统计信息如下:

Plan hash value: 1873025285
 
----------------------------------------------------------------------------------------------------
| Id  | Operation          | Name                          | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT   |                               |    35M|  1670M|   385G  (1)|999:59:59 |
|   1 |  UPDATE            | UUU_UIU_ITTTTTTTTT            |       |       |            |          |
|*  2 |   TABLE ACCESS FULL| UUU_UIU_ITTTTTTTTT            |    35M|  1670M|   559K  (1)| 01:51:56 |
|*  3 |   TABLE ACCESS FULL| ZZZ_Z_ZZZZ_ZZ                 |     2 |    56 | 10783   (1)| 00:02:10 |
|*  4 |   TABLE ACCESS FULL| ZZZ_ZIZ_ZZZZ_ZZZZ_INFO_ZZZZZZ |     1 |    35 |     3   (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------

 

表的相关信息特别重要,所以我定制脚本去获取执行计划中所涉及的所有表相关的信息

 

问题非常明显:
1.要UPDATE的表UUU_UIU_ITTTTTTTTT表有3.5kw条数据  也就意味着ZZZ_Z_ZZZZ_ZZ SW 和 VW_DDIDD_DDDDD F 会被通过TABLE ACCESS FULL分别访问3.5kw次
而且VW_DDIDD_DDDDD F 是一个视图,视图里面所有涉及到的表(庆幸这个视图里面就一张表)同样会被访问 3.5kw次。所以这个SQL执行会执行很长时间。而且不会报错。
2.DPDATE的这种用另一个表的列去更新主表的写法,SQL只能走嵌套循环,而且只能选择UUU_UIU_ITTTTTTTTT S(3.5kw条数据)作为驱动表
所以要解决这个问题。只能用改写语句

解决:改写语句需要通过开发了解业务
开发反馈:通过关联ZZZ_Z_ZZZZ_ZZ SW表 WHERE SW.ZZZZ_NNN = S.MA_ZZZZ_NN 为了补录 UUU_UIU_ITTTTTTTTT 表里面缺失的数据,

也就是【匹配到数据才更新】
而VW_DDIDD_DDDDD F 这个表相当于参数表,取出汇率相乘作运算
到这里。我才发现
这个SQL不仅在执行效率上非常坑爹  ,根本就是一条完全业务上完全错误的SQL 
她这个SQL的结果是UUU_UIU_ITTTTTTTTT S表通过ZZZ_Z_ZZZZ_ZZ SW去更新:【匹配到更新,匹配不到会更新成空值】。DPDATE经常容易出现这种错误
庆幸这个SQL么有跑出结果, 否则被更新的字段将会出现很多NULL值。当然 还有一个坑爹之处 我到后面才发现,往下看


用MERGE来改写UPDATE,最终的结果就是希望SQL走HASH连接,来解决因为驱动表返回的行数太多而引发的性能问题

我尝试了一下,没搞定【老司机可以尝试一下】
1.ZZZ_Z_ZZZZ_ZZ SW和VW_DDIDD_DDDDD F视图没法关联
2.MERGE和UPDATE一样没法更新不固化的视图
所以思考了一下 根据业务拆分成两条SQL

MERGE INTO UUU_UIU_ITTTTTTTTT S
USING (SELECT SW.ZZZZ_NNN,LOSS_BAL FROM ZZZ_Z_ZZZZ_ZZ SW where ETL_DATE = :B1) SW
on (SW.ZZZZ_NNN = S.MA_ZZZZ_NN)
when matched then
  UPDATE SET S.ASSET_LOSS_PRVS = LOSS_BAL
   WHERE S.GL_ACCT_ID IS NOT NULL;

COMMIT;

MERGE INTO UUU_UIU_ITTTTTTTTT S
USING (SELECT F.CONVERSION_RATE,CURRENCY_CD
         FROM VW_DDIDD_DDDDD F
        WHERE CONVERSION_DATE = :B1) F
ON (F.CURRENCY_CD = S.CURRENCY_CD)
when matched then
  UPDATE 
     SET S.ASSET_LOSS_PRVS = S.ASSET_LOSS_PRVS * F.CONVERSION_RATE
      WHERE S.GL_ACCT_ID IS NOT NULL;
COMMIT;
执行计划如下
Plan hash value: 3526885530
 
---------------------------------------------------------------------------------------------------
| Id  | Operation            | Name               | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------------
|   0 | MERGE STATEMENT      |                    |  2783K|  1242M|       |  1261K  (1)| 04:12:24 |
|   1 |  MERGE               | UUU_UIU_ITTTTTTTTT |       |       |       |            |          |
|   2 |   VIEW               |                    |       |       |       |            |          |
|*  3 |    HASH JOIN         |                    |  2783K|  1117M|   106M|  1261K  (1)| 04:12:24 |
|*  4 |     TABLE ACCESS FULL| ZZZ_Z_ZZZZ_ZZ      |  2783K|    74M|       | 10818   (1)| 00:02:10 |
|   5 |     TABLE ACCESS FULL| UUU_UIU_ITTTTTTTTT |    35M|    13G|       |   561K  (1)| 01:52:15 |
---------------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   3 - access("SW"."ZZZZ_NNN"="S"."MA_ZZZZ_NN")
   4 - filter("ETL_DATE"=:B1)


Plan hash value: 2040901539
 
------------------------------------------------------------------------------------------------------
| Id  | Operation            | Name                          | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------
|   0 | MERGE STATEMENT      |                               |    19M|  8811M|   561K  (1)| 01:52:17 |
|   1 |  MERGE               | UUU_UIU_ITTTTTTTTT            |       |       |            |          |
|   2 |   VIEW               |                               |       |       |            |          |
|*  3 |    HASH JOIN         |                               |    19M|  7840M|   561K  (1)| 01:52:17 |
|*  4 |     TABLE ACCESS FULL| ZZZ_ZIZ_ZZZZ_ZZZZ_INFO_ZZZZZZ |     6 |   210 |     3   (0)| 00:00:01 |
|   5 |     TABLE ACCESS FULL| UUU_UIU_ITTTTTTTTT            |    35M|    13G|   561K  (1)| 01:52:15 |
------------------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   3 - access("A"."SRC_CCY_CD"="S"."CURRENCY_CD")
   4 - filter("A"."TARGET_CCY_CD"='T01' AND "A"."EXCH_RATE_TYP_CD"='GLS0_Corporate' AND 
              "A"."ETL_DATE"=:B1)

通过和开发确认,修改后的语句符合业务逻辑。可以更新存储过程

 

在将这个语句放入存储过程的时候,我发现了另一个问题:UPDATE之后没有使用COMMIT语句【在存储过程最后是有一个COMMIT】
但是这样的写法会导致很大问题。【每个事务之后都应该COMMIT,否则小事务就会慢慢变成大事务。开发规范明确要求避免大事务】

修改后性能大幅度提升,重新调度存储过程 前后仅用了不到10分钟搞定

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值