详解MySQL(InnoDB)如何处理死锁

详解MySQL(InnoDB)如何处理死锁,第1张

锁是需要事务结束后才释放的。

一个是 MVCC,一个是两阶段锁协议。

为什么要并发控制呢?是因为多个用户同时 *** 作 MySQL 的时候,为了提高并发性能并且要求如同多个用户的请求过来之后如同串行执行的一样(为了解决脏读、不可重复读、幻读)

官方定义:

两阶段锁协议是指所有事务必须分两个阶段对数据加锁和解锁,在对任何数据进行读、写 *** 作之前,事务首先要获得对该数据的封锁;在释放一个封锁之后,事务不再申请和获得任何其他封锁。

对应到 MySQL 上分为两个阶段:

但是两阶段锁协议不要求事务必须一次将所有需要使用的数据加锁(innodb在需要的索引列数据才锁行),并且在加锁阶段没有顺序要求,所以这种并发控制方式会形成死锁。

MySQL有两种死锁处理方式:

死锁检测 (默认开启)

死锁检测的原理是构建一个以事务为顶点、锁为边的有向图,判断有向图是否存在环,存在即有死锁。

回滚

检测到死锁之后,选择插入更新或者删除的行数最少的事务回滚,基于 INFORMATION_SCHEMA.INNODB_TRX 表中的 trx_weight 字段来判断。

收集死锁信息:

减少死锁:

死锁解决:

索引 KEY_TSKTASK_MONTIME (STATUS_ID MON_TIME)

分析 涉及的两条语句应该不会涉及相同的TSK_TASK记录 那为什么会造成死锁呢?

查询MySQL官网文档 发现这跟MySQL的索引机制有关 MySQL的InnoDB引擎是行级锁 我原来的理解是直接对记录进行锁定 实际上并不是这样的

要点如下:

不是对记录进行锁定 而是对索引进行锁定

在UPDATE DELETE *** 作时 MySQL不仅锁定WHERE条件扫描过的所有索引记录 而且会锁定相邻的键值 即所谓的next key locking

如语句UPDATE TSK_TASK SET UPDATE_TIME = NOW() WHERE ID >会锁定所有主键大于等于 的所有记录 在该语句完成之前 你就不能对主键等于 的记录进行 *** 作

当非簇索引(non cluster index)记录被锁定时 相关的簇索引(cluster index)记录也需要被锁定才能完成相应的 *** 作

再分析一下发生问题的两条SQL语句 就不难找到问题所在了

当 update TSK_TASK set STATUS_ID= UPDATE_TIME=now () where STATUS_ID= and MON_TIME

假设 update TSK_TASK set STATUS_ID= UPDATE_TIME=now () where ID in ( ) 几乎同时执行时 本语句首先锁定簇索引(主键) 由于需要更新STATUS_ID的值 所以还需要锁定KEY_TSKTASK_MONTIME 的某些索引记录

这样第一条语句锁定了KEY_TSKTASK_MONTIME 的记录 等待主键索引 而第二条语句则锁定了主键索引记录 而等待KEY_TSKTASK_MONTIME 的记录 在此情况下 死锁就产生了

笔者通过拆分第一条语句解决死锁问题

先查出符合条件的ID select ID from TSK_TASK where STATUS_ID= and MON_TIME <date_sub(now() INTERVAL minute) 然后再更新状态 update TSK_TASK set STATUS_ID= where ID in (… )

至此 死锁问题彻底解决

lishixinzhi/Article/program/MySQL/201311/29601


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/8499126.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-17
下一篇 2023-04-17

发表评论

登录后才能评论

评论列表(0条)

保存