记一次数据库锁表处理心路历程

接下任务

这是平凡的一天,但是今天,我接到了一个艰巨的任务!
领导给我发信息了!
财务系统经常出现数据库锁表,最近出现频率特别高,已长达有一年之久。希望我能解决掉这个巨大的隐患!

当时,我一头雾水,这个系统从没接触过,代码写的怎样,数据库设计的怎样,具体业务逻辑是怎么,我都不知道。但是,领导既然找我处理,必是遇到了难过的坎,我必须解决!

于是,脑子中立即浮现了能产生锁表的所有情况:
1)事务开启后,没有提交或回滚
2)并发竞争相同的数据库资源,造成相互等待
3)还有其他情况吗?

当天下午,我动手了!
拉取了所有的代码,折腾了好一会儿,本地运行起来了,开始熟悉代码。
这是个前后端分离的项目,后台通过springMVC暴露restfull接口,前端是AngularJs。

尝试解决

从代码上看不出明显的错误,还得从日志着手,先看看日志吧。
惊喜来了,服务器上怎么找不到日志,这下懵了!
原来是log4j日志配置有误,导致服务器上根本就没记录日志文件,于是顺手把日志问题解决了。
第二天,终于可以看见系统运行日志了。

日志输出太少,看不到有用的信息,在锁表的时候,只看见事务超时了,但是多数是卡在查询事务上,难道是查询太慢了吗?不可能啊,查询太慢的话,顶多也就系统慢一点而已,不可能造成锁表。

好吧,继续看代码!
又一惊喜!后台怎么没使用spring注解事务,xml配置事务也没有!难道这个系统就没使用事务?
咨询项目老员工得知,后台使用的是手动事务。在需要的地方手动开启事务,然后执行完成后,提交事务,如果抛异常或执行失败了,就回滚事务。

“这什么思想,为什么要这么做?”。
老员工无奈的表示:“我也不知道,这项目做的时候,我还没来呢!”
“我们把手动事务去掉,改成自动事务吧,让spring来管理事务,肯定比手动管理要靠谱”
“可以啊,但是项目中已经有非常多的地方这么用了,改起来会不会有风险?”
我心想,你说的也有道理,财务系统比较重要,既然系统已经运行这么长时间了,还是不宜大动干戈,稳扎稳打会比较好点,毕竟我也没有明确的证据证明就是手动事务造成的。
好的,历史遗留问题!

小有收获

死马当活马医,先把眼前明显的性能问题解决了再说!
加缓存吧!我用了guava,给所有的配置数据加上了一层缓存。这个改动花了不少时间,每次需要和需求人员确定后才能改动。
期间还发现了ibatis(你没看错,这里用的是ibatis,不是mybatis)嵌套查询老是会出现事务超时。
全部改掉吧!将这些嵌套查询,全部改成service里面再查询一次,如果是配置数据就改成缓存查询,我就不信还会查询超时。

查询超时没有了,系统稳定了一些,锁表问题有所改善!
从之前的每天一大早就锁表,一天锁表好几次;变成现在每天中午左右锁表,一天锁表一两次。
总算有所安慰,但是问题还未解决,前面的改动治标不治本!

被锁的表仍然千奇百怪,但是有几个表特别频繁被锁。
先研究这些重点表吧!果然有收获,从代码中发现,有个service方法,前后多次修改了同一张表,还有可能是同一条记录。这在并发情况下,完全满足锁表条件。
好的,就是你了,改掉吧,拆分成两个事务!
第二天,没锁表了。问题会不会就这样解决了呢!我心里也在打鼓。不大可能吧,锁的表又不止这个!
“这次要是解决了,我到静安寺烧个香!”同事冒出调侃的声音,显然也略显无奈!
果然,到第三天下班的时候,群里消息来了“看看是不是锁表了,系统又不能用了”
真的锁表了,噩耗还是来了!逃避是不可能了,勇敢面对现实吧。

天道酬勤

事到如今,没有更好的办法,只能看代码,一定是手动事务使用不当导致的!
搜索了所有使用手动事务的地方,我一行行代码看过去(应该有100多处使用手动事务,有的方法还特别长)。
皇天不负苦心人!我发现有两个地方事务开起来,没有提交或回滚,这里要是调用了,锁表是必然的!
又过一天,我如约期待锁表的消息,但主观上我又不希望得到消息,希望问题就此解决。下午6点多,消息来了!又是噩耗!
好吧,换掉c3p0链接池,这个连接池不稳定,Druid会好很多,还有监控可以方便查看连接池情况!
当天晚上,我们换了连接池!
“我今天晚上到静安寺烧个香吧,希望这次能够解决!”同事又胆颤的冒了一句。
可惜,当天晚上发布失败了,可能是代码没合并好。先回滚代码吧,今晚发布取消。
“你去晚了,应该一下班就去烧香”又一同事调侃了一句。
第二天,我开个新的代码分支,本地合并好代码提交了,今晚继续发布。
终于发布成功了!连接池终于换了,但也只是个自我安慰,我又怎么会不清楚锁表问题和c3p0到底会不会有关系。

问题解决

又隔几天,我惊喜的发现,现在锁表非常聚焦,基本就是那两三张表了。
找到了!和这些表有关系的,有可能造成锁表的就这两个方法了,安排两个测试人员在demo环境做并发试试吧。
demo环境锁表了!在界面上同时点击之后产生的。顿时群里爆炸了!
这是质的飞跃!找到问题了,距离解决也就不远了!

我左看右看,上看下看,来回看了好几遍。这代码到底有什么问题呢!
眼尖的同事指出:“这个地方有个判断,直接返回了,没有提交或回滚事务,应该是这里导致的。”
恍然大悟!肯定是这个问题,之前一直关注事务打开后,在最后有没有提交或回滚,而忽略了,在中间也有可能有问题!
改进之后,继续测试,demo环境没锁表了。问题解决了!
但是别高兴太早,直觉告诉我,应该还有问题!既然这个方法有这种问题,系统中别的地方应该有类似的问题!
于是,我又搜索了所有的手动事务代码,一行行看看过去!又找到了四个类似的问题,这下应该算是解决了!
第二天,我安静的等待消息,锁表的消息。
群里有人@我,“帮忙看一下是不是锁表了”,老天爷真会开玩笑,难道还有问题!
“没有锁表,有惊无险”,看一下日志,是有个异常导致的!这是业务异常,不是锁表!
第一次看见业务异常我是这么开心!(这是什么思想呢!)

峰回路转

之后的连续好几天系统正常运行!大家都欢呼雀跃,心想这个鬼问题终于解决了!
但是,好景不长。
到了,第七天,群里来消息了:“看看有没有锁表,系统不能用了”
这是早上10:30,星期五,本来以为可以好好度个周末,可是就像有人狠狠地抽了我一个大嘴巴,瞬间精神了。
真的锁表了,先解锁了,让用户能用再说。
查看日志,发现从10:19开始,有非常多的慢sql日志,我下意识的问了一句:“10:19有没有动过数据库,感觉系统慢了很多”,群里议论了一番,没人动过。
当时我一头雾水,又审查了两遍事务代码,所有的事务隐患已经全部改掉了,不可能再有事务问题,这叫我怎么办呢!
没过多久,大概半个小时时间,系统又出问题了。感觉自己也快奔溃了,不能让这事情继续发生,我连上服务器,感觉有点不对劲,好像比平时慢很多。
服务器CPU100%了,事务锁表不可能造成这个问题的,一定是其他问题,比如死循环或者大数据查询等。
还是看看日志吧!有GC频繁回收异常,这是内存不够用啊,有线索了,问题马上就能找到!
仔细往回翻日志,我发现有个附件表的全表查询,这是一个千万级记录的超级大表,全表查询必定撑爆内存,好的,肯定是这个问题!
回头看10:19分的日志,果然,当时也有一个一样的sql语句!问题确定了。
整个人都放松了,终于搞清楚了!

经验总结

这个任务从我接手到解决,经历了近三个月,主要做了如下改动:
1)修改系统log4j日志
2)增加许多配置数据缓存
3)修改ibatis嵌套查询
4)修改所有未关闭的事务代码
5)大表全表查询,导致服务器内存不足,进而导致整个服务器变慢

总结下来,关键问题还是如刚开始的预测“问题肯定出在事务上”,但是要找到问题并解决问题,真要历尽劫难,甚至开始怀疑自己的常识!

问题千奇百怪,总有解决办法,过程千辛万苦,又有几人能懂!

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

顽石九变

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值