2017-11-08 6 views
6

次のシナリオをデバッグしようとしています。MySQL同時挿入により、(明示的な)トランザクションの外部でデッドロックが発生する

スキーマを次のように(?私はInnoDBは/自動内部コミットワット単一の文としてこれを扱うと仮定しても)これは、明示的なトランザクションの外部で行われます:あなたは上のユニーク索引がある見ることができるように

CREATE TABLE locks (
    id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, 
    lock_uid varchar(255) NOT NULL, 
    count smallint(6) NOT NULL, 
    processor_id varchar(255) DEFAULT NULL, 
    created_at timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', 
    updated_at timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', 
    PRIMARY KEY (id), 
    UNIQUE INDEX locks_lock_uid_unique (lock_uid) 
) 

lock_uidを使用して、同じ値を持つ複数の行がテーブルに含まれないようにします。

スレッド1:

insert into `locks` (`lock_uid`, `count`, `processor_id`, `created_at`, `updated_at`) 
values ('11161567', '0', NULL, '2017-11-07 10:46:36', '2017-11-07 10:46:36') 

コマンドが実行され(コンテキストのために、これらは完全に正気のための一般的なクエリログから取得され、照合コマンドの外側のいずれかのスレッドには他の文はありません)

スレッド2:

insert into `locks` (`lock_uid`, `count`, `processor_id`, `created_at`, `updated_at`) 
values ('11161567', '0', NULL, '2017-11-07 10:46:36', '2017-11-07 10:46:36') 

これは、次のデッドロックになった:

LATEST DETECTED DEADLOCK 
------------------------ 
2017-11-07 10:46:36 0x2ac88f791700 
*** (1) TRANSACTION: 
TRANSACTION 6089510736, ACTIVE 0 sec inserting 
mysql tables in use 1, locked 1 
LOCK WAIT 4 lock struct(s), heap size 1136, 4 row lock(s), undo log entries 1 
MySQL thread id 177584962, OS thread handle 47059008030464, query id 13109086103 ec2-34-232-58-13.compute-1.amazonaws.com 34.232.58.13 appserver update 
insert into `locks` (`lock_uid`, `count`, `processor_id`, `created_at`, `updated_at`) values ('11161567', '0', NULL, '2017-11-07 10:46:36', '2017-11-07 10:46:36') 
*** (1) WAITING FOR THIS LOCK TO BE GRANTED: 
RECORD LOCKS space id 6403 page no 4 n bits 176 index locks_lock_uid_unique of table `core`.`locks` trx id 6089510736 lock_mode X locks gap before rec insert intention waiting 
Record lock, heap no 107 PHYSICAL RECORD: n_fields 2; compact format; info bits 32 
0: len 30; hex 636865636b6f75745265636f7665727950726f636573735f313131363137; asc 111617; (total 32 bytes); 
1: len 8; hex 0000000003266637; asc  &f7;; 

*** (2) TRANSACTION: 
TRANSACTION 6089510734, ACTIVE 0 sec inserting 
mysql tables in use 1, locked 1 
4 lock struct(s), heap size 1136, 5 row lock(s), undo log entries 1 
MySQL thread id 177584971, OS thread handle 47040888903424, query id 13109086092 ec2-34-237-3-244.compute-1.amazonaws.com 34.237.3.244 appserver update 
insert into `locks` (`lock_uid`, `count`, `processor_id`, `created_at`, `updated_at`) values ('11161567', '0', NULL, '2017-11-07 10:46:36', '2017-11-07 10:46:36') 
*** (2) HOLDS THE LOCK(S): 
RECORD LOCKS space id 6403 page no 4 n bits 176 index locks_lock_uid_unique of table `core`.`locks` trx id 6089510734 lock mode S 
Record lock, heap no 104 PHYSICAL RECORD: n_fields 2; compact format; info bits 32 
0: len 30; hex 636865636b6f75745265636f7665727950726f636573735f313131363135; asc 111615; (total 32 bytes); 
1: len 8; hex 0000000003266632; asc  &f2;; 

Record lock, heap no 105 PHYSICAL RECORD: n_fields 2; compact format; info bits 32 
0: len 30; hex 636865636b6f75745265636f7665727950726f636573735f313131363135; asc 111615; (total 32 bytes); 
1: len 8; hex 0000000003266634; asc  &f4;; 

Record lock, heap no 107 PHYSICAL RECORD: n_fields 2; compact format; info bits 32 
0: len 30; hex 636865636b6f75745265636f7665727950726f636573735f313131363137; asc 111617; (total 32 bytes); 
1: len 8; hex 0000000003266637; asc  &f7;; 

*** (2) WAITING FOR THIS LOCK TO BE GRANTED: 
RECORD LOCKS space id 6403 page no 4 n bits 176 index locks_lock_uid_unique of table `core`.`locks` trx id 6089510734 lock_mode X locks gap before rec insert intention waiting 
Record lock, heap no 107 PHYSICAL RECORD: n_fields 2; compact format; info bits 32 
0: len 30; hex 636865636b6f75745265636f7665727950726f636573735f313131363137; asc 111617; (total 32 bytes); 
1: len 8; hex 0000000003266637; asc  &f7;; 

*** WE ROLL BACK TRANSACTION (2) 

私は同様の回答を読んでいます(例: MySQL locking in Duplicate Key Error)、このケースで何が起こっているのかよく分かりません。説明がデッドロック出力と一致しません。

  • トランザクション外の2つの挿入でこのデッドロックが発生するのはなぜですか?
  • Xに要求する前にT2がSロックを保持しているのはなぜですか?
+0

「autocommit」の設定をもう一度確認してください。 'id'の必要性はありますか? –

+0

潜在的に。私たちはORMを使用する代理キーがあれば、私たちのORMが私たちの意志に向かってかなり容易になるというトレードオフを持っています。 – AndySavage

+0

私は2つの 'ユニーク'キー(1つが 'PRIMARY')を持つことが問題の一部であると心配しています。しかし、私はそれを証明することはできません。毎秒何百ものINSERTをやっていますか? –

答えて

0

挿入クエリは、共有OR排他ロックを割り当てる前に挿入意図ロックを使用しています。例については

: スキーマ:

CREATE TABLE `temp_table` (
    `id` INT NOT NULL AUTO_INCREMENT, 
    `u_id` INT NULL DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    INDEX `u_id` (`u_id`) 
) 
COLLATE='latin1_swedish_ci' 
ENGINE=InnoDB; 

が、私はテーブル内の2,4,7- U_ID存在を持っていることができます。今私は別のセッションから2つの挿入操作をしています。

以下は可能です。

ケース1:

T1:TEMP_TABLE(U_ID)の値( '5')に挿入します。

T2:temp_table(u_id)の値( '6')に挿入します。

このトランザクションは、最初にギャップインサート意図ロックを適用し、割り当てられるロックのタイプ(SまたはX)を確認します。ここでは5 & 6がギャップ(4-7)に収まるので、排他ロックはT1 & T2に割り当てられます。

ケース2:

T1:TEMP_TABLE(U_ID)の値( '5')に挿入します。

T2:temp_table(u_id)の値( '5')に挿入します。

ここで、インサート意図ロックT1はギャップ(4-7)に収まり、排他ロックを割り当てますが、排他ロックが既に割り当てられているため、ギャップは(5-7)に更新されてギャップとしてフィットしませんT1にT2が割り当てられ、共有ロックが割り当てられ、T1の完了を待機します。

ここで、T1が成功した場合、T2は重複キーエラーとみなされますが、何らかの理由でT1が失敗した場合、T2は排他ロックされ、レコードを挿入しようとします。あなたがMySQLのガイドをREFEREすることができます詳細については

https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html

+0

2トランザクションの場合にデッドロックが発生する可能性については説明していません(@AndySavageが3トランザクションの場合と異なります) – ffeast

+0

デッドロックの詳細については、以下のリンクを参照してください。 https://www.percona.com/blog/2014/10/28/how-to-deal-with-mysql-deadlocks/ – Jack

2

なぜ、このデッドロックは、トランザクション外で2つのインサート上で発生しますか。

は、それはまだ InnoDB内https://dev.mysql.com/doc/refman/5.7/en/innodb-autocommit-commit-rollback.html

は、すべてのユーザーのアクティビティがトランザクションの内部で発生したトランザクションを持っています。 自動コミットモードが有効になっている場合は、各SQLステートメントは、T2はすでにSロックを保持しないのはなぜだからあなたの単一のクエリは短命取引

として扱うことができ

独自に単一 のトランザクションを形成しますXを要求する前に、同じT1はどこにいないのですか?

です。これはちょうど最新のデッドロック情報を出力する関数がどのように働くかです:-) 最初のトランザクションで保持されているロックは印刷されません。 2つの並列mysqlセッションからシンプルなデッドロックをシミュレートすることで、自分で確認することができます。 そして、ここではコードです:

https://github.com/mysql/mysql-server/blob/5.7/storage/innobase/lock/lock0lock.cc#L7236

DeadlockChecker::notify(const lock_t* lock) const 
{ 
    ut_ad(lock_mutex_own()); 

    start_print(); 

    print("\n*** (1) TRANSACTION:\n"); 

    print(m_wait_lock->trx, 3000); 

    print("*** (1) WAITING FOR THIS LOCK TO BE GRANTED:\n"); 

    print(m_wait_lock); 

    print("*** (2) TRANSACTION:\n"); 

    print(lock->trx, 3000); 

    print("*** (2) HOLDS THE LOCK(S):\n"); 

    print(lock); 

    /* It is possible that the joining transaction was granted its 
    lock when we rolled back some other waiting transaction. */ 

    if (m_start->lock.wait_lock != 0) { 
     print("*** (2) WAITING FOR THIS LOCK TO BE GRANTED:\n"); 

     print(m_start->lock.wait_lock); 
    } 

    DBUG_PRINT("ib_lock", ("deadlock detected")); 
} 

説明はデッドロック出力

に一致していませんそれはあなたのケースで何が起こっているように見えるものにかなり近いです。 はここにシミュレーションです:

準備:

create table test (id int primary key, val int not null unique) engine=innodb; 
insert into test values (1, 1), (2, 2), (3, 3); 

今すぐオープン3つの殻をすることができますし、次のコマンドを実行します。

1> begin; 
2> begin; 
3> begin; 

、その後

1>insert into test values (1, 1); 
2>insert into test values (1, 1); (will hang) 
3>insert into test values (1, 1); (will hang) 

となりまし

1>rollback 
2>would produce: ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction 
3>would produce: Query OK, 1 row affected 

1>show engine innodb status; 
... 
------------------------ 
LATEST DETECTED DEADLOCK 
------------------------ 
2017-11-15 23:21:47 0x700000d95000 
*** (1) TRANSACTION: 
TRANSACTION 2336, ACTIVE 8 sec inserting 
mysql tables in use 1, locked 1 
LOCK WAIT 4 lock struct(s), heap size 1136, 2 row lock(s) 
MySQL thread id 8, OS thread handle 123145316831232, query id 58 localhost 127.0.0.1 root update 
insert into test values (1, 1) 
*** (1) WAITING FOR THIS LOCK TO BE GRANTED: 
RECORD LOCKS space id 26 page no 3 n bits 72 index PRIMARY of table `test`.`test` trx id 2336 lock_mode X insert intention waiting 
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0 
0: len 8; hex 73757072656d756d; asc supremum;; 

*** (2) TRANSACTION: 
TRANSACTION 2335, ACTIVE 13 sec inserting 
mysql tables in use 1, locked 1 
4 lock struct(s), heap size 1136, 2 row lock(s) 
MySQL thread id 5, OS thread handle 123145316552704, query id 57 localhost root update 
insert into test values (1, 1) 
*** (2) HOLDS THE LOCK(S): 
RECORD LOCKS space id 26 page no 3 n bits 72 index PRIMARY of table `test`.`test` trx id 2335 lock mode S 
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0 
0: len 8; hex 73757072656d756d; asc supremum;; 

*** (2) WAITING FOR THIS LOCK TO BE GRANTED: 
RECORD LOCKS space id 26 page no 3 n bits 72 index PRIMARY of table `test`.`test` trx id 2335 lock_mode X insert intention waiting 
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0 
0: len 8; hex 73757072656d756d; asc supremum;; 

*** WE ROLL BACK TRANSACTION (2) 

あなたの結果に近いですよね。

関連する問題