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