Efter att ha läst lite mer upptäckte jag att, eftersom InnoDB använder radnivålåsning, kan dödlägen uppstå när man bara infogar eller uppdaterar en enskild rad eftersom åtgärderna inte är atomära. Jag sprang:
SHOW ENGINE INNODB STATUS
för att hitta information om det senaste dödläget. Jag hittade:
------------------------
LATEST DETECTED DEADLOCK
------------------------
140106 17:22:41
*** (1) TRANSACTION:
TRANSACTION 63EB5222A, ACTIVE 0 sec starting index read
mysql tables in use 3, locked 3
LOCK WAIT 9 lock struct(s), heap size 3112, 6 row lock(s), undo log entries 2
MySQL thread id 4304350, OS thread handle 0x7fd3b74d3700, query id 173460207 192.168.0.2 sharecash Updating
UPDATE `click_rollups` SET `clicks` = `clicks` + 1, `last_updated` = '1389046961' WHERE `camp_id` = '27739' AND `country` = 'US' AND `clicks` < '1000' AND `time_created` = '1389046866'
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 186 page no 407 n bits 1272 index `country` of table `sharecash`.`click_rollups` trx id 63EB5222A lock_mode X waiting
*** (2) TRANSACTION:
TRANSACTION 63EB52225, ACTIVE 0 sec fetching rows
mysql tables in use 3, locked 3
177 lock struct(s), heap size 31160, 17786 row lock(s), undo log entries 2
MySQL thread id 4304349, OS thread handle 0x7fd6961c8700, query id 173460194 192.168.0.1 sharecash Updating
UPDATE `click_rollups` SET `clicks` = `clicks` + 1, `last_updated` = '1389046961' WHERE `camp_id` = '30949' AND `country` = 'US' AND `clicks` < '1000' AND `time_created` = '1388964767'
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 186 page no 407 n bits 1272 index `country` of table `sharecash`.`click_rollups` trx id 63EB52225 lock_mode X
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 186 page no 512 n bits 384 index `PRIMARY` of table `sharecash`.`click_rollups` trx id 63EB52225 lock_mode X locks rec but not gap waiting
*** WE ROLL BACK TRANSACTION (1)
Du kan se att de två frågorna som orsakar dödläget faktiskt är exakt samma. Det visar att det också finns olika parametrar för kolumnerna i WHERE-satsen, så de faktiska raderna som låses är olika, vilket verkade lite kontraintuitivt för mig - hur kan operationer på olika uppsättningar rader orsaka ett dödläge?
Svaret verkar vara att dödläget uppstår från frågemotorns låsning av poster i indexeringsstrukturerna. Om du tittar på utdata ovan kan du se att en transaktion har ett lås på en viss del av en viss sida i country
index och behöver ett lås på en del av primärnyckelindexet, medan den andra transaktionen i huvudsak är det motsatta fallet.
En invariant i den här delen av vår app att bara en rad någonsin skulle ha mindre än 1000 klick, så jag tror att genom att åtgärda problemet kommer dödlägesproblemet att minimeras, eftersom det totalt sett skulle göras mindre låsning. MySQL-dokumentationen föreslår att du kodar dina applikationer för att alltid återutföra transaktioner i händelse av en återställning på grund av ett dödläge, vilket skulle förhindra detta problem för att orsaka fel på sidor. Men om någon har några andra idéer om hur man faktiskt kan undvika dessa dödlägen, återigen, vänligen skriv dem i kommentarerna!
EDIT -
country
index behövde inte användas av transaktionen, som för varje camp_id
värde det fanns bara en handfull (vanligtvis bara 1) olika värden för country
, som var och en endast motsvarade en rad. Jag har lagt till en indextips till frågan för att få den att sluta använda detta index, och problemet är nu åtgärdat utan någon prestandaträff (förmodligen en liten vinst).