网站开发与应用课程讨论,wordpress升级php,谷歌浏览器最新版本,昆明网络推广方式有哪些本文介绍了一个 MySQL 数据库死锁的案例和解决方案。 场景 生产环境出了一个偶现的数据库死锁问题#xff0c;导致少部分业务处理失败。 分析特征之后#xff0c;发现是多个线程并发执行同一个方法#xff0c;更新关联的数据时可能会出现#xff0c;把场景简化概括一下导致少部分业务处理失败。 分析特征之后发现是多个线程并发执行同一个方法更新关联的数据时可能会出现把场景简化概括一下 有一个数据表 tb1主键名 id有两条 id 分别为 A1 和 A2 的记录对应的外键 fk_biz_no 相同方法 myFunc整体是一个事务方法 myFunc 里的逻辑是先更新 tb1 里的一条记录执行一些逻辑后再更新该记录的外键对应的所有记录 这样 线程1 和 线程2 并发执行 myFunc 方法时示意如下 线程1 先更新 A1此时会对 A1 所在行加写锁再更新 A1 和 A2此时会同时给 A1 和 A2 所在行都加上写锁 线程2 先更新 A2此时会对 A2 所在行加写锁再更新 A1 和 A2此时会同时给 A1 和 A2 所在行都加上写锁。 如此一来如果出现类似以下的执行时序则会形成死锁 带着一点伪装的 ABBA 死锁。 解决方案 按照消除死锁条件的思路一般会想到将两个线程里的加锁顺序改为一致但是此场景并不完全适用。以下是几种可行的方案 方案一、对 myFunc 方法加分布式锁可以用需要更新的记录的 fk_biz_no 作为锁的 key这样同一个 fk_biz_no 的更新操作就会串行执行 方案二、在方法/事务的最开始就提前把 A1A2 的写锁申请到比如 SELECT ... FOR UPDATE然后再执行后续逻辑 方案三、优化 myFunc 方法里的逻辑先将 A1 和 A2 的数据都处理好了然后一次性更新 A1A2即将方法里的两次更新合并成一次更新 方案一 和 方案二 效果类似都是使同一 fk_biz_no 的更新操作串行了而方法三则是消除了 ABBA 的情况实际场景中有可能需要考虑并发执行下的数据混乱、数据覆盖的问题那是另外的话题了在此不展开。 小结 来一起复习下死锁的四个必要条件 互斥条件一个资源每次只能被一个进程使用请求与保持条件一个进程因请求资源而阻塞时对已获得的资源保持不放不剥夺条件进程已获得的资源在末使用完之前不能强行剥夺循环等待条件若干进程之间形成一种头尾相接的循环等待资源关系。 预防和消除死锁的思路则无非是消除上述四个条件中的一个或多个。