Depois de fazer mais algumas leituras, descobri que, como o InnoDB usa bloqueio em nível de linha, bloqueios podem ocorrer ao inserir ou atualizar apenas uma única linha, pois as ações não são atômicas. eu corri:
SHOW ENGINE INNODB STATUS
para encontrar informações sobre o último impasse. Eu encontrei:
------------------------
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)
Você pode ver que as duas consultas que estão causando os deadlocks são exatamente as mesmas. Isso mostra que também existem parâmetros diferentes para as colunas na cláusula WHERE, portanto, as linhas reais que estão sendo bloqueadas são diferentes, o que parecia um pouco contra-intuitivo para mim - como as operações em diferentes conjuntos de linhas podem causar um impasse?
A resposta parece ser que o impasse está surgindo das entradas de bloqueio do mecanismo de consulta nas estruturas de indexação. Se você observar a saída acima, poderá ver que uma transação tem um bloqueio em uma determinada parte de uma determinada página no
country
index e precisa de um bloqueio em parte do índice de chave primária, enquanto a outra transação é essencialmente o caso oposto. Uma invariável nesta parte do nosso aplicativo que apenas uma linha teria menos de 1.000 cliques, então acredito que ao corrigir esse problema, o problema de impasse será minimizado, pois haveria menos bloqueios em geral. A documentação do MySQL sugere codificar seus aplicativos para sempre reemitir transações no caso de um rollback devido a um deadlock, o que evitaria que esse problema causasse erros nas páginas. No entanto, se alguém tiver alguma outra ideia sobre como evitar esses impasses, novamente, poste-a nos comentários!
EDITAR -
O
country
index não precisava ser usado pela transação, como para cada camp_id
valor, havia apenas um punhado (geralmente apenas 1) valores diferentes de country
, cada qual correspondendo apenas a uma linha. Eu adicionei uma dica de índice à consulta para fazê-la parar de usar esse índice, e o problema agora foi corrigido sem nenhum impacto no desempenho (provavelmente um pequeno ganho).