вопрос про тупик InnoDB в MySQL? - PullRequest
3 голосов
/ 10 марта 2010

Я обнаружил такую ​​интересную проблему в движке 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, в потоке 1:

    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 завершает запрос на выборку, поскольку 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 ]

1 голос
/ 10 июня 2010

Почему вы даже хотите выполнить этот SQL вместо

REPLACE INTO SeqNum VALUES (NULL); 
SELECT last_insert_id();
0 голосов
/ 10 марта 2010

«SELECT ... FOR UPDATE» помещает блокировку INTENTION EXCLUSIVE (IX) в таблицу SeqNum и устанавливает блокировку EXCLUSIVE (X) на все строки, соответствующие критериям SELECT.

Состояние замков можно увидеть с помощью монитора замков Innodb. Это можно сделать, создав таблицу со специальным именем:

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

Состояние блокировок отображается при каждом выполнении следующей команды:

show engine innodb status \G

Когда первый поток выполняет «Выбрать ... для обновления», устанавливаются следующие блокировки (у меня есть одна строка со значением 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 - одна для пропуска (супремум) после единственной строки, а другая для фактической строки данных.

Когда во втором потоке выполняется «выбрать ... для обновления», добавляются следующие блокировки:

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

...