Après avoir lu un peu plus, j'ai découvert que, comme InnoDB utilise le verrouillage au niveau des lignes, des blocages peuvent se produire lors de l'insertion ou de la mise à jour d'une seule ligne, car les actions ne sont pas atomiques. J'ai couru :
SHOW ENGINE INNODB STATUS
pour trouver des informations sur le dernier blocage. J'ai trouvé :
------------------------
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)
Vous pouvez voir que les deux requêtes à l'origine des blocages sont en fait exactement les mêmes. Cela montre qu'il existe également différents paramètres pour les colonnes dans la clause WHERE, de sorte que les lignes réelles qui sont verrouillées sont différentes, ce qui me semblait un peu contre-intuitif - comment des opérations sur différents ensembles de lignes pourraient-elles provoquer un blocage ?
La réponse semble être que le blocage provient des entrées de verrouillage du moteur de requête dans les structures d'indexation. Si vous regardez la sortie ci-dessus, vous pouvez voir qu'une transaction a un verrou sur une certaine partie d'une certaine page dans le country
index et nécessite un verrou sur une partie de l'index de clé primaire, tandis que l'autre transaction est essentiellement le cas opposé.
Un invariant dans cette partie de notre application qu'une seule ligne aurait jamais moins de 1000 clics, donc je pense qu'en résolvant ce problème, le problème de blocage sera minimisé, car il y aurait globalement moins de verrouillage. La documentation MySQL suggère de coder vos applications pour toujours réémettre les transactions dans le cas d'une annulation due à un blocage, ce qui empêcherait ce problème de provoquer des erreurs de pages. Cependant, si quelqu'un a d'autres idées sur la façon d'éviter ces impasses, encore une fois, merci de les publier dans les commentaires !
MODIFIER -
Le country
index n'a pas besoin d'être utilisé par la transaction, comme pour chaque camp_id
valeur il n'y avait qu'une poignée (généralement juste 1) valeurs différentes de country
, dont chacun ne correspondait qu'à une ligne. J'ai ajouté un indice d'index à la requête pour qu'elle cesse d'utiliser cet index, et le problème est maintenant résolu sans aucun impact sur les performances (probablement un petit gain).