案例背景

环境:功能测试环境,Linux + WAS 6.1 + DB2 9.5
问题描述:功能测试组反馈某APP上报业务一直失败,无法成功。

分析过程

查看后台日志,出现如下错误,调用cm的ICmOmDivisionServiceEx的setStationDivision方法时,调用失败,总共调用了3次。

性能问题分析调优案例第1篇_性能调优案例

查询数据库锁等待情况,发现CM_PM_FRM_FUND表存在锁等待,无限时的锁等待(未设置锁等待超时)。。。

性能问题分析调优案例第1篇_性能调优案例_02

此时,跟CM_PM_FRM_FUND表有关的查询模块一直处于转圈圈的状态,这是因为数据被X锁了,其他想要查询的会话均处于NS锁等待状态。

既然有锁等待,那么先查询X锁了种植主体的会话正在执行的SQL,命令:

db2 get snapshot for applications on TSO >tsoapp.txt

如果是DB2 v9.7以上版本,可以通过db2top监控。
 
从tsoapp.txt中搜索导致会话堵塞的会话 Application handle = 21952
查看该会话正在执行的SQL(Dynamic SQL statement text:):

select s.frm_cd,

       m.division_unique_cd,

       m.certificate_no,

       m.certificate_type_ek,

       m.psn_name                            ,

       m.frm_member_info_id,

       m.frm_fund_unique_id,

       m.member_unique_id,

       m.hf_relation_ek,

       m.if_sell_qualfy                      ,

       m.share_rate,

       m.plow_area,

       m.share_amt,

       m.appoint_area,

       m.appoint_wght,

       m.famy_psn_qty,

       m.work_psn_qty                        ,

       m.out_work_psn_qty,

       m.send_state,

       m.modify_time,

       m.data_state,

       m.biz_year,

       m.photo_id,

       m.sex_ek,

       m.MEN_TECH_GRADE_EK,

       m.MEN_EDUCATION_EK,

       m.MEN_MOBILE

  from cm_pm_frm_member_info m

  left join cm_pm_frm_fund s

    on (m.member_unique_id = s.frm_fund_unique_id and s.biz_year = 2017 and

       s.data_state = '1')

 where m.data_state = '1'

   and m.frm_fund_unique_id = 'a44df2d5857b4657bf316150d37e5bef'

   and m.biz_year = 2017

该SQL的执行速度并不慢,在DB2客户端下执行在1秒以内就返回了。从数据库会话状态来看,它并没有处于被堵塞的状态。
 
再从tsoapp.txt中搜索被堵塞会话正在执行的SQL,发现它们都正在执行下面这句SQL:

insert into cm_om_div_org_rela
  (div_org_rela_id,
   org_unique_cd,
   division_unique_cd,
   begin_year,
   end_year,
   send_state,
   modify_time,
   data_state,
   biz_type_ek)
  (select sys_guid(),
          m.station_unique_cd,
          m.division_unique_cd,          2017,          9999,          '0',          '2017-05-08 09:31:14',          '1',          'CM'                   from view_cm_om_frm_fund m 
          where m.biz_year = 2017 and m.station_unique_cd = '11520306000000000003' 
          and m.division_unique_cd not in
           (select s.division_unique_cd            from cm_om_div_org_rela s            where s.BIZ_TYPE_EK = 'CM'
            and s.org_unique_cd = '11520306000000000003'
            and s.data_state = '1'
            and s.begin_year <= 2017
            and s.end_year > 2017))

该SQL在上报过程中执行,确实会处于锁等待状态。而释放掉锁后再去执行,速度也很快。
 
分析锁数据,发现是21952这个会话X锁了CM_PM_FRM_FUND表数据,而其它会话需要对这些数据进行NS查询,因此进入锁等待状态。但是我们当前请求的只有一个上报操作,为什么会产生这样的锁等待呢?

跟踪线程快照,卡住的代码位置与WAS日志中报错信息的位置一致,都是pm的setStationDivision调用cm的setStationDivision时,出现了调用失败。
 
那么,会不会是这种情况呢?
上报数据的时候,PM先把某条CM_PM_FRM_FUND的记录给X锁(Insert或者Update)了未提交,然后在同一个事务里面,又调用CM的setStationDivision接口,而该接口执行insert into cm_om_div_org_rela这个语句时,会对CM_PM_FRM_FUND表进行一次汇总查询需要加NS锁,于是这个语句就进入了锁等待状态,CM的setStationDivision接口在等待PM释放之前X锁上的那条CM_PM_FRM_FUND记录,而PM在等待CM接口的返回结果,然后就一直死等下去了,这是另外一种形式的死锁啊。

解决问题

开发优化程序代码,不在PM中调用CM的setStationDivision接口。

案例小结

这里涉及到几个知识点:

  1. dubbo服务调用时,默认是会重试三次的,这个要小心(比如Insert 的业务如果重试三次会有什么后果)
  2. DB2默认隔离级别下,查询是要上NS共享查询锁的,NS锁与X锁是不能兼容的;
  3. DB2的锁等待可以设置超时时间,如果没设置,默认是无限期等待下去;