2016-06-20 10 views
1

Ich habe eine Java-Anwendung, und es tritt Deadlock Ausnahme auf der sql unten: insert into voucher ( id, order_id, voucher_code ) SELECT #{id}, #{orderId}, #{voucherCode} FROM DUAL WHERE NOT EXISTS (SELECT id FROM voucher where order_id = #{orderId}) Die "order_id" ist eindeutiger Schlüssel. Und ich bin sicher, dass es blockiert wird, wenn der SQL-Server im Parallelbetrieb ausgeführt wird. Allerdings habe ich keine Berechtigung, um show engine innodb status auszuführen, damit ich Informationen über die Deadlock-Ausnahme nicht abrufen kann.mysql innodb Deadlock Grund finden auf INSERT INTO SELECT

Ich versuche, das Problem in einer Laborumgebung zu reproduzieren. Tabellentest ist unten: Create Table: CREATE TABLE `test` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `info` varchar(128) NOT NULL DEFAULT '', `order_id` int(11) NOT NULL DEFAULT '0', PRIMARY KEY (`id`), UNIQUE KEY `uni_order_id` (`order_id`) ) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARSET=utf8

und führe ich zwei sqls in zwei verschiedenen Sitzungen:

insert into test(info,order_id) select '12345',sleep(10) from dual where not exists (select info from test where info='12345'); 
insert into test(info,order_id) select '12345',234 from dual where not exists (select info from test where info='12345'); 

Deadlock log unten:

------------------------ 
LATEST DETECTED DEADLOCK 
------------------------ 
2016-06-20 17:26:54 700000a83000 
*** (1) TRANSACTION: 
TRANSACTION 2321, ACTIVE 9 sec inserting 
mysql tables in use 2, locked 2 
LOCK WAIT 5 lock struct(s), heap size 1184, 2 row lock(s) 
MySQL thread id 1, OS thread handle 0x700000a3f000, query id 29 localhost root executing 
insert into test(info,order_id) select '12345',234 from dual where not exists (select info from test where info='12345') 
*** (1) WAITING FOR THIS LOCK TO BE GRANTED: 
RECORD LOCKS space id 9 page no 3 n bits 72 index `PRIMARY` of table `test`.`test` trx id 2321 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 2320, ACTIVE 10 sec setting auto-inc lock 
mysql tables in use 2, locked 2 
3 lock struct(s), heap size 360, 1 row lock(s) 
MySQL thread id 2, OS thread handle 0x700000a83000, query id 28 localhost root User sleep 
insert into test(info,order_id) select '12345',sleep(10) from dual where not exists (select info from test where info='12345') 
*** (2) HOLDS THE LOCK(S): 
RECORD LOCKS space id 9 page no 3 n bits 72 index `PRIMARY` of table `test`.`test` trx id 2320 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: 
TABLE LOCK table `test`.`test` trx id 2320 lock mode AUTO-INC waiting 
*** WE ROLL BACK TRANSACTION (2) 

ich angenommen, dass die Unterabfrage select info from test where info=‘12345’ Slock halten kann und insert into … select wollen XLock. Aber ich finde das offizielle Dokument nicht, um meinen Standpunkt zu unterstützen.

Also meine Frage ist unten:
1. ist mein reproduzieren Design richtig?
2. ist mein Vorschlag (die Unterabfrage select info from test where info=‘12345’ kann SLock halten) richtig? Jedes offizielle Dokument kann meinen Vorschlag unterstützen?

Antwort

1

Wenn Sie INSERT ... SELECT ausführen, sperrt MySQL standardmäßig alle Zeilen in SELECT. Wenn Sie die Isolationsstufe in READ-COMMITTED ändern, sind Zeilen in SELECT nicht gesperrt. In Ihrem Fall sollte dies das Deadlock-Problem lösen.

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; 

PS: Ich schlage vor, über Isolationsstufen zu lesen und Unterschiede zwischen ihnen zu verstehen.

http://dev.mysql.com/doc/refman/5.7/en/innodb-locks-set.html

INSERT INTO T ... SELECT FROM S WHERE ... setzt eine exklusive Index Satzsperre (ohne Spalt Sperre) auf jeder Zeile in T. eingefügt, wenn die Transaktionsisolationsstufe READ COMMITTED, oder innodb_locks_unsafe_for_binlog ist aktiviert und die Transaktion Isolationsstufe ist nicht SERIALIZABLE, InnoDB führt die Suche auf S als konsistent lesen (keine Sperren). Andernfalls setzt InnoDB freigegebenen next-key Sperren auf Zeilen von S. InnoDB muss Sperren im letzteren Fall festlegen: In aktualisierende Wiederherstellung von einer Sicherung muss jede SQL-Anweisung auf die gleiche Weise ausgeführt werden, wie es getan wurde ursprünglich.

CREATE TABLE ... SELECT ... führt das SELECT mit gemeinsamen Next-Key- -Sperren oder als eine konsistente Lese, wie für INSERT ... SELECT.

Wenn ein SELECT in den Konstrukten verwendet wird REPLACE INTO t SELECT ... FROM s WHERE ... oder UPDATE t ... WHERE col IN (SELECT FROM ... s ...), InnoDB Sätze shared next-key sperrt Zeilen aus Tabellen s.

+0

Ich möchte den Grund für den Deadlock in diesem Fall anstelle der Lösung finden. – FatGhosta

0

Sie arbeiten zu hart.Ich denke, tut das Gleiche:

INSERT IGNORE INTO voucher 
    (id, order_id, voucher_code) 
    VALUES 
    (#{id}, #{orderId}, #{voucherCode}) 

Wenn orderId bereits in der Tabelle ist, werden die UNIQUE Schlüssel beschweren, aber IGNORE wird es zum Schweigen bringen.

Aber ... Sie haben ein anderes Problem ... Ihre Abfrage (und meine) überprüft id für ein Duplikat. Das heißt, es kann 2 Dinge geben, die UNIQUE treffen, aber Sie prüfen für nur einen.

Normalerweise, würde man einfach id auslassen:

INSERT IGNORE INTO voucher 
    (order_id, voucher_code) 
    VALUES 
    (#{orderId}, #{voucherCode}) 

Aber Sie wollen wahrscheinlich die neue id erhalten, wenn der Einsatz stattfindet? So ...

INSERT INTO voucher 
    (order_id, voucher_code) 
    VALUES 
    (#{orderId}, #{voucherCode}) 
    ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID(id); 
SELECT LAST_INSERT_ID(); 

Wenn auf der anderen Seite wollen Sie wirklich mit beiden eindeutigen Schlüsseln zu spielen, sollten Sie diese unordentlich Aussage:

REPLACE INTO voucher 
    (id, order_id, voucher_code) 
    VALUES 
    (#{id}, #{orderId}, #{voucherCode}) 

Das wird

  1. DELETE beliebige Zeile (n), die übereinstimmen entweder eindeutiger Schlüssel (ID oder order_id)
  2. INSERT die Werte. (Da Sie id angegeben, AUTO_INCREMENT hat keine Auswirkung.)

Nun lassen Sie uns dies noch einen Schritt weiter tragen. Warum haben zwei eindeutige Schlüssel? Kannst du id nicht komplett loswerden? Und dann machen order_id die PRIMARY KEY?

Jetzt haben Sie das Schema vereinfacht, die Abfrage vereinfacht und sehr wahrscheinlich den Deadlock beseitigt.

Verwandte Themen