暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

长事务问题的解决

老吕架构 2021-11-05
1286

背景


        长事务在DB服务端的表现是session持续时间长,期间可能伴随cpu、内存升高,严重者可导致DB服务端整体响应缓慢,导致在线应用无法使用,所以在线高并发业务中应该尽量避免长事务的发生。产生长事务的原因除了sql本身可能存在问题外,和应用层的事务控制逻辑也有很大的关系,下面老吕来简单分析下。


应用层与DB的交互逻辑


1、发送第一条sql时从连接池获取一个DB连接,开启手动事务,发送sql;

2、收到DB响应;

3、在同一个DB连接上继续发送一条sql;

4、收到DB响应;

......

......

5、不断的重复“发送sql+接收响应”的操作;

6、捕获到异常,发送回滚指令,响应回滚成功;

7、没有任何异常,发送提交指令,响应提交成功;

8、释放连接回到连接池(此时其它线程可获取使用);


长事务的影响


1、对DB服务端的影响:

对数据库服务端资源的占用(连接数、内存、cpu),增加数据库服务端的压力;

2、对DB客户端的影响:

1)长时间占用连接导致应用层连接池中的连接数不够用;

2)长事务期间应用层线程资源会一直被占用,堵塞式编程中会形成堵塞等待;

3)高并发场景下线程堵塞会造成连锁反应、雪崩效应;


如何解决长事务问题?


1、增加对长事务的监控,记录长事务的logId,根据logId能查询到整个请求调用链日志,可以明确是哪个服务的哪个接口的哪个方法产生的;(如何实现长事务的监控,等老吕的另一篇文章)


2、到底是哪步产生了等待?日志中详细记录了长事务中每条sql的开始执行时间,通过分析可以知道是哪两个sql之间间隔较长;


3、看上一条sql是否真的慢;


4、看代码,这两条sql之间的代码有什么逻辑,是不是发生了耗时的操作,比如本地调用堵塞或者RPC堵塞等;


5、分析非数据库操作代码堵塞的原因,并解决;


6、增加防护措施:

1)事务持续期间避免耗时的非数据库操作,比如复杂的本地逻辑计算或者IO操作或者RPC操作;

2)增加超时控制,比如RPC操作最多等待3秒,如果没有响应则抛异常,这样才能避免雪崩效应;

3)减小事务的粒度,由大变小,甚至去除:

没有DML语句的代码要关闭事务或者标记为只读事务;

只有一条DML语句的,关闭手动事务,使用自动事务;

多条DML语句的开启事务,开启事务的时机是第一条DML语句执行时,

提交事务的时机是最后一条DML sql执行完成后尽快关闭;


7、Spring框架下对事务的控制方法:

1)使用aop配置或者@Transactional注解,粒度较大,使用方便;

2)使用TransactionTemplate的编程式事务灵活控制事务的范围,粒度精确,只是使用上繁琐一些

(高并发系统推荐使用);


8、只有一条DML SQL的场景下,是否真的需要手动事务?

MySQL默认是RR事务级别,在此期间所有的查询都会产生数据快照,这肯定是占用内存的,还要去管理它,

这个读一致性对你接下来的DML语句有没有必要呢,在此期间如果发生数据变更,对你的DML语句会有什么影响。

事实上不会有任何影响,不管你有没有开启读一致性,都不会对DML语句的结果有影响。

即使有并发冲突,这个读一致性也解决不了并发问题,真要解决并发问题还是要靠悲观锁和乐观锁方案;

悲观锁方案可以使用锁表、锁记录等(select + for update);

乐观锁方案使用版本号或者时间戳,对于insert 语句要靠 唯一约束控制并发,对于update和delete要靠 version字段控制并发。


9、行并发控制的必要性

这是要根据业务场景来的,绝对多数场景下,不会产生行并发,对某些严密功能和关键数据可以考虑行并发的控制,普通数据不需要考虑,当然目前有的持久化框架有统一的版本控制机制,对业务层无感知 。


10、应用手动事务的场景:最少要向DB发送两条 DML  SQL语句的场景下才有必要开启手动事务;


11、有多条DQL SQL的方法中能不能开启手动事务:有些场景需要依赖可重复读的隔离级别,这时候可以开启手动事务,但是记着标记为只读事务( ReadOnly )来进行优化。



长事务的监控数据


贴几个老吕开发的长事务监控数据

案例1

概要:
gl_service服务,事件:长事务
容器信息:
gl_service;service_gl_8485696c49_tc7rg;172.31.0.245
其它信息:
LimitDto{logId='40eca24d10b2441192638815ebb9ca5b', serviceName='null', url='null', interfaceName='null', methodName='null',


eventTime='2021-11-05T09:21:04.010', eventType='长事务', args='SQL详情:


transaction begin time:1636075233976


current time:1636075233976,select 1 from gl_common_doc_template where orgId = ? and docTemplateCode = ? limit 1;;


current time:1636075233977,select 1 from gl_common_doc_template where orgId = ? and docTemplateName = ? limit 1;;


------在此期间发生了什么导致了30秒的等待?


current time:1636075264002,SELECT max(orderNo) FROM gl_common_doc_template WHERE orgId=?;;


current time:1636075264006,insert into gl_common_doc_template (
orgId, id, docTemplateId, docTemplateCode,
docTemplateName, docType, creatorId,
creator, createTime, summary,
exchangeRate, origAmountDr, origAmountCr,
amountDr, amountCr, quantityDr,
quantityCr, price, unitid,
rowNo, currencyId, accountId,
departmentId, personId, customerId,
supplierId, inventoryId, projectId,
exCalc1,exCalc2,exCalc3,exCalc4,exCalc5,
exCalc6,exCalc7,exCalc8,exCalc9,exCalc10,orderNo)
values ......;', result='null',


timeCost=30034}

案例2

概要:
biz_bovms_service服务,事件:长事务
容器信息:
biz_bovms_service;service_biz_bovms_fb8dd65f5_9l9xn;172.31.2.19
其它信息:
LimitDto{logId='4af295537cef42eda50212f258a64f76', serviceName='null', url='null', interfaceName='null', methodName='null',


eventTime='2021-11-05T09:24:24.659', eventType='长事务', args='SQL详情:


transaction begin time:1636075443989


current time:1636075443990,SELECT FROM biz_st_acctcode_setup
WHERE 1=1 AND org_id=? AND module=? AND vat_type=?;


current time:1636075444026,select from biz_st_product_share where
orgId = ? and period = ? order by period desc;

---期间发生了什么??看代码


current time:1636075464643,select * from biz_st_carry_produce_sheet where
orgId = ? and period = ? order by period;


current time:1636075464647,insert into biz_st_carry_produce_sheet(orgId,id,period,inventoryId,putInNum,price,putInCost,materialFee,
personCost,directCost,otherExpenses,ts,costRate,bomMaterialFee)
values
(?,?,?,?,
?,?,?,?,
?,?,?,?,?,?);


current time:1636075464653,INSERT INTO biz_st_carry_over_voucher ( orgId , id , period , voucherId , type )
VALUES (?, ?, ?, ?, ?);;', result='null',


timeCost=20670}

案例3

概要:
xc_core_service服务,事件:长事务
容器信息:
xc_core_service;service_xc_core_754bfc6bdf_f6d9s;172.31.9.33
其它信息:
LimitDto{logId='3ed28497754e4eac8c924256eae0b358', serviceName='null', url='null', interfaceName='null', methodName='null',


eventTime='2021-11-05T09:24:24.229', eventType='长事务', args='SQL详情:


transaction begin time:1636075443567


current time:1636075443578,select from xc_xcb_sq where id= ?;


current time:1636075443582,select from xc_xcb_sq where orgId= ? and xclx= ? and sdqjq = str_to_date(?,'%Y-%m-%d');


current time:1636075443587,select xcb.,ry.xm as ryxm, ry.rzsgrq as rzsgrq from xc_xcb_zcgzxj xcb, xc_ryxx ry where xcb.sqid= ? and xcb.zt >= 0 and xcb.ryid = ry.id;


current time:1636075443591,select xcb.,ry.xm as ryxm, ry.rzsgrq as rzsgrq from xc_xcb_zcgzxj xcb, xc_ryxx ry where xcb.sqid= ? and xcb.zt >= 0 and xcb.ryid = ry.id;


current time:1636075443594,select lx.,zb.RYID as ryid from xc_zxfjkc_zb zb, xc_zxfjkc_lx lx where zb.RYID in ( ? ) and zb.yxbz = 'Y' and zb.nsnd = ? and zb.id = lx.zbid and lx.yxbz = 'Y';


current time:1636075443613,select from wh_sskcs where SDXM_DM = ?;


--期间发生了什么事情????


current time:1636075464201,select from swjgxx where nf=? and find_in_set(?, swjgmc) limit 1;


current time:1636075464205,SELECT id,orgId,qyid,zjlx,zjhm,xm,xb,csny,gjdq,ryzt,sfcjlsgl,gh,dzyx,lxdh,lxdz,qtzjlx,qtzjhm,yjljsj,yzbm,rylx,bz,rzsglx,rzsgrq,lzrq,sfcj,sfls,sfgl,cjhm,lshm,yxbz,csd,xl,zy,jnywjs,jzdz,jzdzs,jzdzsi,jzdzx,hjdz,hjdzs,hjdzsi,hjdzx,lxdzs,lxdzsi,lxdzx,khyh,yhzh,scrjsj,grtze,grtzbl,xmzw,cuser,ctime,muser,mtime,sfyzzt,xtly,bszt,deptid,deptname,jnzw FROM xc_ryxx WHERE id = ?;


current time:1636075464209,INSERT INTO xc_xcb_zcgzxj ( id,sqid,ryid,zzlx,zzlxzw,ryxm,zzhm,sdqjq,sdqjz,sre,jtbz,rcjj,qt,qqkx,qtkk,yfgzhj,jbylf,jbybf,sybxf,bcylbx,bcybxf,grsbxj,zfgjj,znjyzc,zfdklxzc,zfzjzc,sylrzc,jxjyzc,gsjbylf,gsjbybf,gssybxf,gsbcylbx,gsbcybxf,shybxf,gsbxf,gssbxj,gszfgjj,gsrlcb,syjkx,zykcjze,ljsre,ljfdkce,ljzxkchj,ljzxfjkchj,ljynssde,yyjse,ynse,ybtse,sfhj,zt,cjsj,xgsj,sjly ) VALUES ( ?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,? );


current time:1636075464213,select from xc_xcb_sq where id= ?;


current time:1636075464215,select count(*) as nsrs, sum(gsrlcb) as gsrlcbze, sum(sfhj) as srze,sum(ybtse) as ynse from xc_xcb_zcgzxj where sqid= ? and zt >= 0;


current time:1636075464218,UPDATE xc_xcb_sq SET orgId = ?,nsrsbh = ?,xclx = ?,xclxmc = ?,sdqjq = ?,sdqjz = ?,nsrs = ?,gsrlcbze = ?,srze = ?,ynse = ?,pzh = ?,zt = ?,cjsj = ?,xgsj = ? WHERE id = ?;', result='null',


timeCost=20662}

案例4

概要:
biz_core_service服务,事件:长事务
容器信息:
biz_core_service;service_biz_core_57bfb7f7bf_cnspn;172.31.2.16
其它信息:
LimitDto{logId='1220e4677c9d48b2974ca0d1584eb0d3', serviceName='null', url='null', interfaceName='null', methodName='null',


eventTime='2021-11-05T09:23:31.753', eventType='长事务', args='SQL详情:


transaction begin time:1636075388547


current time:1636075388547,select ? as orgId, id, pid, code, fullCode, name, helpCode, helpCodeFull
, accountId, accountCode, accountClassification4BA, calcObject, taxRateId, smallScaleTaxRateId
, isCategory, 1 as isSystem, canDelete, isEnable, canDisable, matchedKey
from biz_core_business_type_preset
where accountingStandardsId = ? and isSimplifyUse = ?;

current time:1636075388562,SELECT orgId,id,pid,code,fullCode,name,helpCode,helpCodeFull,accountId,accountCode,accountClassification4BA,calcObject,taxRateId,smallScaleTaxRateId,isCategory,isSystem,canDelete,isEnable,canDisable,matchedKey,ts
FROM biz_core_business_type
WHERE orgId = ?;'

--说明后面还有其它非数据库耗时操作,导致事务提交延迟----


timeCost=23206}



文章转载自老吕架构,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论