Po dalším čtení jsem zjistil, že jelikož InnoDB používá zamykání na úrovni řádků, může dojít k uváznutí při pouhém vložení nebo aktualizaci jednoho řádku, protože akce nejsou atomické. Běžel jsem:
SHOW ENGINE INNODB STATUS
najít informace o posledním mrtvém bodě. Našel jsem:
------------------------
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)
Můžete vidět, že dva dotazy, které způsobují zablokování, jsou ve skutečnosti přesně stejné. Ukazuje to, že existují také různé parametry pro sloupce v klauzuli WHERE, takže skutečné řádky, které se zamykají, se liší, což se mi zdálo trochu kontraintuitivní – jak by operace na různých sadách řádků mohly způsobit uváznutí?
Zdá se, že odpovědí je, že k uváznutí dochází v důsledku zamykání položek dotazovacího modulu ve strukturách indexování. Pokud se podíváte na výstup výše, můžete vidět, že jedna transakce má zámek na určité části určité stránky v country
index a potřebuje zámek na část indexu primárního klíče, zatímco druhá transakce je v podstatě opačný případ.
Invariant v této části naší aplikace, že pouze jeden řádek by měl někdy méně než 1000 kliknutí, takže věřím, že vyřešením tohoto problému bude problém se zablokováním minimalizován, protože by se celkově provedlo méně zamykání. Dokumentace MySQL doporučuje kódovat aplikace tak, aby vždy znovu zadávaly transakce v případě vrácení zpět kvůli uváznutí, což by tomuto problému zabránilo způsobovat chyby stránek. Pokud by však někdo měl nějaké další nápady, jak se těmto patovým situacím ve skutečnosti vyhnout, opět je napište do komentářů!
UPRAVIT -
country
index nemusela transakce používat, jako u každého camp_id
Hodnota country
měla pouze několik (obvykle jen 1) různých hodnot , z nichž každý odpovídal pouze jednomu řádku. Do dotazu jsem přidal nápovědu k indexu, aby přestal používat tento index, a problém je nyní vyřešen bez jakéhokoli zásahu do výkonu (pravděpodobně malý zisk).