关于MySQL中InnoDB死锁的问题?

关于MySQL中InnoDB死锁的问题?,mysql,deadlock,innodb,Mysql,Deadlock,Innodb,我在MySQL InnoDB引擎中发现了这种有趣的问题,有人能解释一下为什么该引擎总是声称这是一个死锁吗 首先,我创建了一个具有单行、单列的表: CREATE TABLE `SeqNum` (`current_seq_num` bigint(30) NOT NULL default '0', PRIMARY KEY (`current_seq_num`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8

我在MySQL InnoDB引擎中发现了这种有趣的问题,有人能解释一下为什么该引擎总是声称这是一个死锁吗

首先,我创建了一个具有单行、单列的表:

   CREATE TABLE `SeqNum` (`current_seq_num` bigint(30) NOT NULL default '0',
                           PRIMARY KEY  (`current_seq_num`)
   ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
   Query OK, 0 rows affected (0.03 sec)

   mysql> insert into SeqNum values (5);
   Query OK, 1 row affected (0.00 sec)
现在,我有两个MySQL连接器线程,在thread1中:

    mysql> begin;
    Query OK, 0 rows affected (0.00 sec)

    mysql> select `current_seq_num` into @curr_seq FROM SeqNum FOR UPDATE;
    Query OK, 1 row affected (0.00 sec)
现在,在thread2中,我做了完全相同的事情:

    mysql> begin;
    Query OK, 0 rows affected (0.00 sec)

    mysql> select `current_seq_num` into @curr_seq FROM SeqNum FOR UPDATE;
在默认innodb_lock_wait_超时之前,thread2只是等待thread1释放其对表的独占锁,这是正常的

但是,在thread1中,如果输入以下更新查询:

     mysql> update SeqNum set `current_seq_num` = 8;
     ERROR 1213 (40001): Deadlock found when trying to get lock; 
     try restarting transaction
现在,thread2完成了select查询,因为thread1退出

此外,在thread1中,如果我使用where子句输入update查询,它可以很好地执行:

     mysql> update SeqNum set `current_seq_num` = 8 where `current_seq_num` =5
     Query OK, 1 row affected (0.00 sec)
有人能解释一下吗?

“选择…进行更新”在SeqNum表上放置一个意向独占(IX)锁,并在与选择条件匹配的所有行上放置一个独占(X)锁

可以使用Innodb锁监视器查看锁的状态。这是通过创建一个特殊命名的表来实现的:

create table innodb_lock_monitor( i int not null ) engine = innodb;
然后,每当发出以下命令时,都会显示锁的状态:

show engine innodb status \G
当第一个线程执行“Select…for update”时,将放置以下锁(表中有一行的值为5):

这是表上的IX锁和两个X锁——一个在唯一行之后的间隙(上确界)上,另一个在实际数据行上

在第二个线程中执行“选择…进行更新”时,将添加以下锁:

TABLE LOCK table `test`.`SeqNum` trx id 0 1285 lock mode IX
RECORD LOCKS space id 0 page no 51 n bits 72 index `PRIMARY` of table `test`.`SeqNum` trx id 0 1285 lock_mode X waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 8; hex 8000000000000005; asc         ;; 1: len 6; hex 000000000503; asc       ;; 2: len 7; hex 800000002d0110; asc     -  ;;
这是表上的新IX锁,加上唯一数据行上的“X等待”锁

原始线程可以在没有死锁的情况下对整个表或唯一的数据行运行更新

它正在运行“5.1.37-1ubuntu5.1”,具有可重复读取功能

见:


为什么要执行此SQL而不是

REPLACE INTO SeqNum VALUES (NULL); 
SELECT last_insert_id();

我认为僵局和可疑僵局之间的区别。这只是猜测,因为它们使用相同的锁,而且需要一段时间才能出现死锁情况。我无法复制您的事件序列(我相信我昨天可以)。现在,对于从线程1执行的任何更新,我都没有死锁。请你用整个序列(包括插入SeqNum)和关于版本和隔离模式的信息更新这个问题好吗?马丁:我用详细的序列事件更新了这个问题。我只是试了一次,以确保它始终是一样的。威廉:我完全按照你的顺序做了,但我没有遇到死锁。也许您正在运行一个有bug的版本。您正在运行哪个版本?马丁:恐怕我不能同意你的看法。请注意,如果添加where子句,则可以执行update查询。我的观点是:如果设置了IX锁的会话不能更新行,那么有IX锁有什么意义呢?你是对的。就在我想我明白了什么的时候…:-)我应该删除这个答案吗?我需要更详细地了解正在发生的事情。你试过在Innodb监视器打开的情况下看这个吗?WilliamLou:我在重新阅读手册后更新了答案。好问题。我现在唯一不明白的是为什么第二个“选择…更新”会被阻止:如果IX锁兼容,为什么第二个锁会被阻止但仍然被授予?马丁:是的,这次你的解释是有道理的,但我还是有点困惑。让我们命名两个线程A和B,我的理解是:A已经有一个IX锁,B想要一个IX锁。这两个锁冲突,因此B等待A释放其IX锁。现在,A想要一个需要X锁的更新(无论有没有where),为什么它不能这么做呢?对于表SeqNum,整个表也是它仅有的一行。
REPLACE INTO SeqNum VALUES (NULL); 
SELECT last_insert_id();