最近遇到一个MYSQL update 语句出现 Deadlock found when trying to get lock 的问题,分析一下原因。

1. 什么情况下会出现死锁?

https://dev.mysql.com/doc/refman/5.6/en/innodb-deadlocks.html

出现死锁需要2个条件:

  1. 至少2个client(A,B)同时在执行事务
  2. clientA锁定了某一行,未提交事务,此时clientB也需要update/delete这一行,此时clientB就会进入等待状态,直到出现Deadlock 。

2. 如何减少死锁的发生?

很重要的两点,就可以避免这种情况

  1. 事务操作锁定的行数较少(更精确的索引条件)。
  2. 保证事务较短的执行时间,完成后马上提交。

这里有更加详细的描述

3. 例子

先建立以下一个表,并导入一些数据

  1. CREATE TABLE `test_dead_lock` (
  2. `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  3. `uid` int(11) NOT NULL DEFAULT '0' COMMENT '用户ID',
  4. `message` varchar(45) NOT NULL DEFAULT '' COMMENT '用户信息',
  5. PRIMARY KEY (`id`)
  6. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='死锁测试'
  7. INSERT INTO `vip_dap`.`test_dead_lock` (`uid`, `message`) VALUES ('1', 'aaa');
  8. INSERT INTO `vip_dap`.`test_dead_lock` (`uid`, `message`) VALUES ('1', 'bbb');
  9. INSERT INTO `vip_dap`.`test_dead_lock` (`uid`, `message`) VALUES ('2', 'ccc');
  10. INSERT INTO `vip_dap`.`test_dead_lock` (`uid`, `message`) VALUES ('2', 'ddd');

clientA执行:

注意,clientA 不要COMMIT

  1. START TRANSACTION;
  2. UPDATE test_dead_lock SET message = 'u1' WHERE `uid`=1 LIMIT 1;

clientB执行

  1. START TRANSACTION;
  2. UPDATE test_dead_lock SET message = 'u2' WHERE `uid`=2 LIMIT 1;
  3. COMMIT;

此时,clientB 更新不会成功,虽然按数据来看,他们更新的行是不同的,但是由于uid上没有索引,这个走不了行锁。
在uid上添加索引后,clientB就可以执行了。

这里用到的策略就是:事务操作锁定的行数较少

  1. ALTER TABLE `vip_dap`.`test_dead_lock`
  2. ADD INDEX `idx_uid` USING BTREE (`uid` ASC);

版权声明:本文为CSDN博主「loophome」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/loophome/article/details/79867174