I'm getting mysql deadlock errors from time to time caused by a race condition. I've managed to replicate the error with the following.
Transaction 1
start transaction insert into fixtradeshistory (select null, fixtrades.* from fixtrades where id=10);Transaction 2
start transaction insert into fixtradeshistory (select null, fixtrades.* from fixtrades where id=10);Transaction 1
update fixtrades set fixtradesstatustypesid='bla', fixgatewayorderid='bla' where id=10;Transaction 2 DEADLOCK
update fixtrades set fixtradesstatustypesid='bla', fixgatewayorderid='bla' where id=10;Any ideas why this deadlock is occuring?
------------------------
LATEST DETECTED DEADLOCK
------------------------
110317 14:52:08
(1) TRANSACTION:
TRANSACTION 0 57841252, ACTIVE 16 sec, process no 2976, OS thread id 3030973328 starting index read`
mysql tables in use 1, locked 1
LOCK WAIT 15 lock struct(s), heap size 1024, undo log entries 1
MySQL thread id 326855, query id 2689051 localhost salert Updating
update fixtrades set fi开发者_StackOverflow中文版xtradesstatustypesid='orderplaced', fixgatewayorderid='BANZAI>EXEC:1288679244240:520703' where id=10
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 0 page no 232059 n bits 136 index PRIMARY` of table `salert/fixtrades` trx id 0 57841252 lock_mode X locks rec but not gap waiting
Record lock, heap no 66 PHYSICAL RECORD: n_fields 26; compact format; info bits 0
0: len 4; hex 8000000a; asc ;; 1: len 6; hex 00000371a2cd; asc q ;; 2: len 7; hex 000004f8400770; asc @ p;; 3: len 4; hex 80000004; asc ;; 4: len 4; hex 80000004; asc ;; 5: len 4; hex 80000364; asc d;; 6: len 4; hex 800040aa; asc @ ;; 7: SQL NULL; 8: len 13; hex 6f726465726163636570746564; asc orderaccepted;; 9: len 30; hex 42414e5a41493e455845433a313238383637393234343234303a35323037; asc BANZAI>EXEC:1288679244240:5207;...(truncated); 10: SQL NULL; 11: len 8; hex 73656c6c6c6f6e67; asc selllong;; 12: len 5; hex 564f442e4c; asc VOD.L;; 13: len 3; hex 313030; asc 100;; 14: SQL NULL; 15: SQL NULL; 16: len 4; hex 4d80e0cc; asc M ;; 17: SQL NULL; 18: SQL NULL; 19: SQL NULL; 20: len 6; hex 6d61726b6574; asc market;; 21: SQL NULL; 22: SQL NULL; 23: SQL NULL; 24: SQL NULL; 25: SQL NULL;
*** (2) TRANSACTION:
TRANSACTION 0 57841255, ACTIVE 7 sec, process no 2976, OS thread id 3030371216 starting index read, thread declared inside InnoDB 500
mysql tables in use 1, locked 1
15 lock struct(s), heap size 1024, undo log entries 1
MySQL thread id 326860, query id 2689066 localhost salert Updating
update fixtrades set fixtradesstatustypesid='orderplaced', fixgatewayorderid='BANZAI>EXEC:1288679244240:520703' where id=10
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 0 page no 232059 n bits 136 index `PRIMARY` of table `salert/fixtrades` trx id 0 57841255 lock mode S locks rec but not gap
Record lock, heap no 66 PHYSICAL RECORD: n_fields 26; compact format; info bits 0
0: len 4; hex 8000000a; asc ;; 1: len 6; hex 00000371a2cd; asc q ;; 2: len 7; hex 000004f8400770; asc @ p;; 3: len 4; hex 80000004; asc ;; 4: len 4; hex 80000004; asc ;; 5: len 4; hex 80000364; asc d;; 6: len 4; hex 800040aa; asc @ ;; 7: SQL NULL; 8: len 13; hex 6f726465726163636570746564; asc orderaccepted;; 9: len 30; hex 42414e5a41493e455845433a313238383637393234343234303a35323037; asc BANZAI>EXEC:1288679244240:5207;...(truncated); 10: SQL NULL; 11: len 8; hex 73656c6c6c6f6e67; asc selllong;; 12: len 5; hex 564f442e4c; asc VOD.L;; 13: len 3; hex 313030; asc 100;; 14: SQL NULL; 15: SQL NULL; 16: len 4; hex 4d80e0cc; asc M ;; 17: SQL NULL; 18: SQL NULL; 19: SQL NULL; 20: len 6; hex 6d61726b6574; asc market;; 21: SQL NULL; 22: SQL NULL; 23: SQL NULL; 24: SQL NULL; 25: SQL NULL;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 0 page no 232059 n bits 136 index `PRIMARY` of table `salert/fixtrades` trx id 0 57841255 lock_mode X locks rec but not gap waiting
Record lock, heap no 66 PHYSICAL RECORD: n_fields 26; compact format; info bits 0
0: len 4; hex 8000000a; asc ;; 1: len 6; hex 00000371a2cd; asc q ;; 2: len 7; hex 000004f8400770; asc @ p;; 3: len 4; hex 80000004; asc ;; 4: len 4; hex 80000004; asc ;; 5: len 4; hex 80000364; asc d;; 6: len 4; hex 800040aa; asc @ ;; 7: SQL NULL; 8: len 13; hex 6f726465726163636570746564; asc orderaccepted;; 9: len 30; hex 42414e5a41493e455845433a313238383637393234343234303a35323037; asc BANZAI>EXEC:1288679244240:5207;...(truncated); 10: SQL NULL; 11: len 8; hex 73656c6c6c6f6e67; asc selllong;; 12: len 5; hex 564f442e4c; asc VOD.L;; 13: len 3; hex 313030; asc 100;; 14: SQL NULL; 15: SQL NULL; 16: len 4; hex 4d80e0cc; asc M ;; 17: SQL NULL; 18: SQL NULL; 19: SQL NULL; 20: len 6; hex 6d61726b6574; asc market;; 21: SQL NULL; 22: SQL NULL; 23: SQL NULL; 24: SQL NULL; 25: SQL NULL;
*** WE ROLL BACK TRANSACTION (2)
Any ideas why this deadlock is occuring?
InnoDB has a number of locking modes. We're seeing a row-level lock here, but it's failing.
Transaction #1 is waiting for an exclusive lock:
RECORD LOCKS ... page no 232059 ... lock_mode X locks rec but not gap waiting
Transaction #2 already has a shared lock on the same row:
RECORD LOCKS ... page no 232059 ... lock mode S locks rec but not gap
While #2 has the shared lock, it also wants an exclusive lock:
RECORD LOCKS ... page no 232059 ... lock_mode X locks rec but not gap waiting
Both #1 and #2 need the same exclusive lock, but neither lock can be granted until #2 releases the shared lock, but #2 can't release the shared lock until it's upgraded to exclusive, but that will never happen.
This causes a deadlock, and InnoDB will kill one of the two transactions and perform a rollback. A similar situation is described on the documentation page I linked.
You have two ways to handle this.
The first and worst way would be to perform table locking instead of using a transaction. This will prevent multiple writers (or optionally readers) from modifying the table at once. This will probably impact performance, and opens up even worse deadlock situations. Further, you can't mix table locks and transactions.
The second and better way is to modify your application to deal with deadlocks elegantly. This means making sure that the rollback happened and either trying again or presenting an appropriate error to the user.
Because transaction 2 holds a S(hared) lock on fixtrades id=10, acquired when it read it, so transaction 1's attempt to get an eXclusive lock to modify the record cannot succeed.
I think you might be able to solve this by changing the insert to this:
insert into fixtradeshistory (select null, fixtrades.* from fixtrades where id=10 FOR UPDATE);
You may also be able to solve the problem by doing the UPDATEs before the INSERTs. This causes transaction 1 to get an exclusive lock the first time around, rather that than the shared lock. Transaction 2 will still be stuck waiting for transaction 1 to complete. But transaction 1 will be able to complete because it will already have the exclusive lock it needs.
See http://vimeo.com/12941188 for a more complete explanation.
精彩评论