【开卷有益】记录一次高并发下的死锁解决思考过程
开卷有益,好久没写博客了,最近工作也挺忙的。 死锁距离我不遥远,终于还是在高并发时被我碰到了。 DeadLock Found! 尽管编程风格中会尽量避免死锁,但是还是被我碰上了。文章可能看不出来我在做什么事情,只是记录自己的一个排除死锁的过程。 事情起源于
开卷有益,好久没写博客了,最近工作也挺忙的。
死锁距离我不遥远,终于还是在高并发时被我碰到了。
DeadLock Found!
尽管编程风格中会尽量避免死锁,但是还是被我碰上了。文章可能看不出来我在做什么事情,只是记录自己的一个排除死锁的过程。
事情起源于两个联动的缓存+redis+异步数据库读写操作。
事务中的这句出现死锁:
DELETE FROM table WHERE FROM key = ‘helloworld’
当初的思考解除死锁的思路如下:
1)分析死锁模型,假设1,2 两个线程,假设A,B两个锁,通俗的说 :
1持有A锁,等待B锁 -- 2 持有B锁,等待A锁
于是掐这了,就瘫了。
2)继续思考,要真这么简单,也不好意思称自己工程师了,实际开发中都是碰到死锁环。什么意思呢?继续假设
1持有A,等B,
2持有B,等C,
3持有C,等A
Anyway,读者可以自己假设还有4,5,6,7...
3)继续思考,死锁出现,第一步肯定是重审自己的代码,这个时候一定要对使用的成熟的工具优先保持自信但要保留质疑的态度,怎么重审,记录我的过程
a,我们说代码越简洁,越不容易出错,所以,秉着DRY原则,也要消除重复的代码,能复用就绝对不看着行数而自得。
b,重构过后,一定是要数据层,逻辑层分离,不要在数据层包含逻辑处理,不要在逻辑层去做数据层做的事情。(这个时候会发现,脑中有设计模式思想和没有这个 概念的人是有很大区别的)
c,回归正题,我代码中的死锁出现是高并发环境,存在热点区,在重构过代码后,肯定要对热点数据进行预处理,才进行异步写入数据库。什么是热点区?我自己乱 叫的一个名字,就是很多时候,日志也好,外来数据也好,很多时候会出现300ms以内出现100条99%相似度的数据。
d,继续测试。发现死锁依旧,立即建立新的缓存,判断到底哪里出了问题。发现,逻辑并没有问题。
e,持着质疑工具的态度,检索使用的工具,高并发发生在异步读写数据库,那么既然逻辑没有问题,读写中操作的数据就不会有问题,那就是数据库这里的锁机制出 了问题。阅读数据库锁机制文档,发现了事务操作中的页面锁和记录锁出了问题。
f,问题来了,既然是多线程,高并发,就不可能不让事务持有记录锁和页面锁,此时怎么办?
解决办法,放弃依据索引删除,更新,改用主键进行删除更新操作。
g,测试,正常。
整个过程,看了很多文档,其中,最有用的:
http://www.2cto.com/os/201408/328321.html文章写完,觉得自己写的挺扯的,好久没写文章了,出来冒个泡泡,表示自己还在。
后续脑袋清醒的时候要做一个高并发的专题,也算对自己这一段工作的总结。同时也会翻译一篇看到的高并发的国外文章。