mysql 的事务支持与存储引擎有关, MyISAM 不支持事务, INNODB 支持事务, 更新时采用的是行级锁这里采用的是 INNODB 做存储引擎, 意味着会将 update 语句做为一个事务来处理前面提到行级锁必须建立在索引的基础, 这条更新语句用到了索引 idx_1, 所以这里肯定会加上行级锁
行级锁并不是直接锁记录, 而是锁索引, 如果一条 SQL 语句用到了主键索引, mysql 会锁住主键索引; 如果一条语句操作了非主键索引, mysql 会先锁住非主键索引, 再锁定主键索引
这个 update 语句会执行以下步骤:
1 由于用到了非主键索引, 首先需要获取 idx_1 上的行级锁
2 紧接着根据主键进行更新, 所以需要获取主键上的行级锁;
3 更新完毕后, 提交, 并释放所有锁
如果在步骤 1 和 2 之间突然插入一条语句: update user_item .....where id=? and user_id=?, 这条语句会先锁住主键索引, 然后锁住 idx_1
蛋疼的情况出现了, 一条语句获取了 idx_1 上的锁, 等待主键索引上的锁; 另一条语句获取了主键上的锁, 等待 idx_1 上的锁, 这样就出现了死锁
解决方案:
1. 先获取需要更新的记录的主键
2. 再更新记录
也就是代码中的应该这样写:
- select id from x_table where id=?
- delete from x_table where id=?
小结: 在采用 INNODB 的 MySQL 中, 更新操作默认会加行级锁, 行级锁是基于索引的, 在分析死锁之前需要查询一下 mysql 的执行计划, 看看是否用到了索引, 用到了哪个索引, 对于没有用索引的操作会采用表级锁如果操作用到了主键索引会先在主键索引上加锁, 然后在其他索引上加锁, 否则加锁顺序相反在并发度高的应用中, 批量更新一定要带上记录的主键, 优先获取主键上的锁, 这样可以减少死锁的发生
来源: http://www.bubuko.com/infodetail-2491741.html