结论优先
从服务能力层面上,我们不允许出现死锁,这是对于系统资源的浪费。
工程结构中不允许出现死锁,我们应该尽可能的进行规避。
死锁的本质
原因
多个线程在并发执行过程中,互相争夺对方<font color="#f44336">已持有</font>的锁,造成“无限期”等待
基本条件
互斥条件:一个资源每次只能被一个进程使用。
请求和保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
不可抢占条件:进程已获得的资源,在末使用完之前,不能强行剥夺,只能在进程使用完时由自己释放。
循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
场景细分
事务中(Mysql为例)
多线程中批量操作单模型数据,顺序乱序,导致互相等待。
多线程中在各自事务场景对于同一批不同模型进行操作,并且顺序并不一致。
非事务(redis为例)
使用Redis实现分布式锁,多线程之间加锁出现乱序交叉,导致互相等待。
更多的
虽然上述实例都在描述多线程,实际单线程也有死锁死锁风险。<br>简单的:<br>1、当所使用redis分布式锁不支持重入,而使用场景中重复获取同一把锁。
问题现象
Redis
因不同线程互相等待,最终会导致链接超时。
Mysql
当两个线程发生死锁时,Mysql会出现死锁异常。
1213 - Deadlock found when trying to get lock; try restarting transaction
特殊的:开始事务后执行一个批量SQL,InnoDB在加行锁时都是做了默认排序后逐个添加,等于Mysql默认帮我们做了排序。此时<font color="#f57c00">无死锁风险</font>。<br>
业务场景需求
等待而非阻断:<br>当多个线程并发对于同一条数据或者同一批数据进行操作时,我们<font color="#e57373">常见</font>的需求场景是接受等待,而非直接阻断业务流程。<br>注:这里跟sql执行结果(即业务执行结果)无关,而是是否执行的策略。<br>
业务可能接受死锁:<br>按上述所说,数据持久层本身不应该也不适合通过异常来控制业务逻辑,但不一定满足不了业务需求,如下述例B。<br>
但是,更多的问题是在我们明明不接受死锁,并且没有任何处理措施的情况下出现了死锁。
实例
例A:库存扣减,多个线程同时对于某一个sku进行库存更新。<br>【特点】在同一个事务中批量更新多条记录,不同线程的批量更新<font color="#4caf50">无业务逻辑上的冲突或依赖</font>,业务结果不唯一。<br>【业务分析】<br>该场景下各个线程依次完成库存更新,不允许出现死锁,即批量数据中有重叠数据时,应该竞争行锁,达到都执行库存更新的结果。此时如果SQL异常,则不满足业务需求。<br>同时,<br>库存的更新有库存扣减、库存返还,其中,库存扣减不一定能执行成功(库存不足),但是在数据持久层中,我们不关心业务逻辑。即数据库只需要按照上述整体策略完成SQL执行,反馈effact rows给上层业务层即可,后续逻辑控制工作由业务层处理。<br>
子主题
子主题
例B:有一批数据原始状态是A,可以同时在两个场景中进行更新,不同场景更新的目标状态不一样分别是B、C,两者皆为终态互相不能流转。<br>【特点】在同一个事务中批量更新多条记录,不同线程的批量更新<font color="#f44336">有业务逻辑上的冲突或依赖</font>,业务结果不唯一。<br>【业务分析】<br>该场景下各个线程依次完成库存更新,此时如果因死锁导致SQL异常,但可以保证一个线程执行成功,而根据业务场景中,无论哪种业务结果皆为终态,则可以认为这种处理<font color="#4caf50">不影响业务。</font><br>但,同上述“基本策略”所述,这种方式虽然满足业务需求,会带来工程结构上的侵入、额外的异常日志,并不推荐。<br>
【细节】上述例A和例B有个重要区别,即例A同一批数据操作中的更新目标值可能并不一样,而例B则本身同一个操作中的同批次数据目标更新值必然相同,带来的实现区别是:<br>例A中更新目标值不一致,则具体的SQL语法无法使用IN,即无法依赖Mysql的自动排序,需要手动额外处理。
解决方案
多模型数据
在保证各个单模型数据的上述解决策略基础上,保障各个模型组合操作在不同场景下的操作顺序固定。
结论
工程中不允许出现死锁,我们应该尽可能的进行规避。
取决于业务需要,有的业务不允许出现死锁,有的可以。
从技术实现层面上讲,应尽可能避免死锁,通过更友好合理的方式来控制业务逻辑。
不应该使用Mysql或者其他等数据持久层中的异常来控制逻辑和业务结果。<br>原因:<br>(1)点对点异常识别成本较大,且会额外造成无用干扰日志。<br>(2)在工程结构中,数据持久层不需要对业务逻辑控制负责。