关于MySQL中InnoDB死锁的问题?

时间:2010-03-10 19:01:26

标签: 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 ;
   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_timeout之前,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子句输入更新查询,它可以很好地执行:

     mysql> update SeqNum set `current_seq_num` = 8 where `current_seq_num` =5
     Query OK, 1 row affected (0.00 sec)

有人能解释一下吗?

2 个答案:

答案 0 :(得分:1)

为什么你甚至想要执行这个SQL而不是

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

答案 1 :(得分:0)

“SELECT ... FOR UPDATE”在SeqNum表上放置一个INTENTION EXCLUSIVE(IX)锁,并在符合SELECT条件的所有行上放置一个EXCLUSIVE(X)锁。

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

create table innodb_lock_monitor( i int not null ) engine = innodb;

只要发出以下命令,就会显示锁的状态:

show engine innodb status \G

当第一个线程执行“Select ... for update”时,会放置以下锁(我在表中有一行值为5):

MySQL thread id 42, query id 338 localhost root
TABLE LOCK table `test`.`SeqNum` trx id 0 1284 lock mode IX
RECORD LOCKS space id 0 page no 51 n bits 72 index `PRIMARY` of table `test`.`SeqNum` trx id 0 1284 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;

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锁 - 一个位于唯一行之后的间隙(supremum)上,另一个位于实际数据行上。

当在第二个线程中执行“select ... for update”时,会添加以下锁定:

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”,带有REPEATABLE-READ。

请参阅:

MySQL Manual - 13.6.8.1. InnoDB Lock Modes