sql >> Base de Datos >  >> RDS >> Mysql

Error de interbloqueo de MySQL

Después de leer un poco más, descubrí que, dado que InnoDB usa el bloqueo a nivel de fila, pueden ocurrir puntos muertos al insertar o actualizar una sola fila, ya que las acciones no son atómicas. Corrí:

SHOW ENGINE INNODB STATUS

para encontrar información sobre el último interbloqueo. Encontré:

------------------------
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)

Puede ver que las dos consultas que están causando los interbloqueos son en realidad exactamente las mismas. Muestra que también hay diferentes parámetros para las columnas en la cláusula WHERE, por lo que las filas reales que se bloquean son diferentes, lo que me pareció un poco contrario a la intuición:¿cómo podrían las operaciones en diferentes conjuntos de filas causar un punto muerto?

La respuesta parece ser que el interbloqueo se debe a que el motor de consulta bloquea las entradas en las estructuras de indexación. Si observa el resultado anterior, puede ver que una transacción tiene un bloqueo en una parte determinada de una página determinada en el country index y necesita un bloqueo en parte del índice de clave principal, mientras que la otra transacción es esencialmente el caso opuesto.

Una invariante en esta parte de nuestra aplicación es que solo una fila tendría menos de 1000 clics, por lo que creo que al solucionar ese problema, el problema de interbloqueo se minimizará, ya que en general se realizarían menos bloqueos. La documentación de MySQL sugiere codificar sus aplicaciones para que siempre vuelvan a emitir transacciones en el caso de una reversión debido a un interbloqueo, lo que evitaría este problema por causar errores en las páginas. Sin embargo, si alguien tiene otras ideas sobre cómo evitar estos puntos muertos, nuevamente, ¡publíquelas en los comentarios!

EDITAR -

El country index no necesitaba ser utilizado por la transacción, como para cada camp_id valor solo había un puñado (generalmente solo 1) valores diferentes de country , cada uno de los cuales solo correspondía a una fila. Agregué una sugerencia de índice a la consulta para que deje de usar este índice, y el problema ahora se solucionó sin ningún impacto en el rendimiento (probablemente una pequeña ganancia).