Obtengo puntos muertos de bloqueos de espacio en una tabla cuando la inserto con frecuencia desde múltiples fuentes. Aquí hay una descripción general de mis procesos.
START TRANSACTION
UPDATE vehicle_image
SET active = 0
WHERE vehicleID = SOMEID AND active = 1
Loop:
INSERT INTO vehicle_image (vehicleID, vehicleImageFilePath, vehicleImageSplashFilePath
,vehicleImageThumbnailFilePath, vehicleImageMiniFilePath, mainVehicleImage, active)
VALUES (%s, %s, %s, %s, %s, %s, 1);
END TRANSACTION
La salida de SHOW Create table vehicle_image;
es:
CREATE TABLE `vehicle_image` (
`vehicleImageID` int(11) NOT NULL AUTO_INCREMENT,
`vehicleID` int(11) DEFAULT NULL,
`vehicleImageFilePath` varchar(200) DEFAULT NULL,
`vehicleImageSplashFilePath` varchar(200) DEFAULT NULL,
`vehicleImageThumbnailFilePath` varchar(200) DEFAULT NULL,
`vehicleImageMiniFilePath` varchar(200) DEFAULT NULL,
`mainVehicleImage` bit(1) DEFAULT NULL,
`active` bit(1) DEFAULT b'1',
`userCreated` int(11) DEFAULT NULL,
`dateCreated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`userModified` int(11) DEFAULT NULL,
`dateModified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`vehicleImageID`),
KEY `active` (`active`),
KEY `mainvehicleimage` (`mainVehicleImage`),
KEY `vehicleid` (`vehicleID`)
) ENGINE=InnoDB AUTO_INCREMENT=22878102 DEFAULT CHARSET=latin1
Y el último punto muerto dado por SHOW engine innodb status
:
LATEST DETECTED DEADLOCK
------------------------
2018-03-27 12:31:15 11a58
*** (1) TRANSACTION:
TRANSACTION 5897678083, ACTIVE 2 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1248, 2 row lock(s), undo log entries 1
MySQL thread id 873570, OS thread handle 0x124bc, query id 198983754 ec2-34-239-240-179.compute-1.amazonaws.com 34.239.240.179 image_processor update
INSERT INTO vehicle_image (vehicleID, vehicleImageFilePath, vehicleImageSplashFilePath, vehicleImageThumbnailFilePath, vehicleImageMiniFilePath, mainVehicleImage, active)
VALUES (70006176, 'f180928(1)1522168276.230837full.jpg', 'f180928(1)1522168276.230837splash.jpg', 'f180928(1)1522168276.230837thumb.jpg', 'f180928(1)1522168276.230837mini.jpg', 1, 1)
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 875 page no 238326 n bits 472
index `vehicleid` of table `ipacket`.`vehicle_image` trx id 5897678083
lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 378 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 4; hex 842c365a; asc ,6Z;;
1: len 4; hex 815d03bc; asc ] ;;
*** (2) TRANSACTION:
TRANSACTION 5897678270, ACTIVE 1 sec inserting, thread declared inside InnoDB 5000
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1248, 2 row lock(s), undo log entries 1
MySQL thread id 873571, OS thread handle 0x11a58, query id 198983849 ec2-35-171-169-21.compute-1.amazonaws.com 35.171.169.21 image_processor update
INSERT INTO vehicle_image (vehicleID, vehicleImageFilePath, vehicleImageSplashFilePath, vehicleImageThumbnailFilePath, vehicleImageMiniFilePath, mainVehicleImage, active)
VALUES (70006326, '29709(1)1522168277.4443843full.jpg', '29709(1)1522168277.4443843splash.jpg', '29709(1)1522168277.4443843thumb.jpg', '29709(1)1522168277.4443843mini.jpg', 1, 1)
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 875 page no 238326 n bits 464
index `vehicleid` of table `ipacket`.`vehicle_image` trx id 5897678270
lock_mode X locks gap before rec
Record lock, heap no 378 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 4; hex 842c365a; asc ,6Z;;
1: len 4; hex 815d03bc; asc ] ;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 875 page no 238326 n bits 472
index `vehicleid` of table `ipacket`.`vehicle_image` trx id 5897678270
lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 378 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 4; hex 842c365a; asc ,6Z;;
1: len 4; hex 815d03bc; asc ] ;;
*** WE ROLL BACK TRANSACTION (2)
Estoy ejecutando muchos de estos procesos simultáneamente, pero nunca ejecuto dos procesos que usan el mismo VehicleID
. Estoy realmente confundido en cuanto a por qué tengo Deadlocks.
He resuelto temporalmente el problema utilizando el nivel de aislamiento READ COMMITTED
, pero he leído que esto requiere cambios en la replicación en el sentido de que debe hacer la replicación a nivel de fila.
He leído otras preguntas aquí que son similares a las mías, pero soy algo nuevo en SQL y todavía no puedo entender por qué ocurre esto.
Preguntas similares:
- Deadlock en declaraciones de inserción de MySQL
- MySQL InnoDB Deadlock para 2 consultas de inserción simples
ACTUALIZAR:
Descubrí que el uso READ COMMITTED
no ha resuelto el problema. Todavía no he descubierto por qué están ocurriendo los puntos muertos y realmente no sé cómo diagnosticar más allá de lo que tengo actualmente. Continúo obteniendo Deadlocks en mi sistema de producción. Cualquier ayuda sería apreciada.
SHOW PROCESSLIST;
. La mayoría de las veces,REPEATABLE READ
es el mejor nivel de aislamiento para la mayoría de las aplicaciones, por lo que no me preocuparía demasiado usarlo. ¿Hubo un aumento notable en el rendimiento cuando lo cambiaste por defectoREPEATABLE READ
?VARCHARs
.loop
es solo un seudocódigo para representar lo que está sucediendo.repeatable read
aread committed
un nivel de aislamiento más bajo que la lectura repetible, pero desafortunadamente no detuvo los puntos muertos. Sé que el hardware afectará al servidor (es una instancia ec2, tendría que buscar detalles), pero no creo que esa información sea necesaria para comprender por qué están ocurriendo los puntos muertos. La naturaleza esporádica de esto también hace que sea difícil capturar la salida de show processlist; cuando se produce el punto muerto.Respuestas:
No soy un experto en MySQL, pero por el aspecto de sus registros de Deadlock, a pesar de que está INSERTANDO diferentes ID de vehículos por declaración, estos requieren que toda la página de datos (238326) del
VehicleID
índice no agrupado esté bloqueada .El hecho de que ocasionalmente tenga puntos muertos significa que dentro de 1 página tiene múltiples ID de vehículos, por lo que hay una pequeña posibilidad de que 2 procesos diferentes necesiten un bloqueo para la misma página.
Lo mejor para aconsejar es mantener sus transacciones lo más pequeñas posible .
Si hay alguna manera de hacer lo siguiente, ayudará a disminuir la posibilidad de un punto muerto:
Si puede, intente cambiar el factor de relleno de ese índice al 95% y pruebe para ver si tiene menos puntos muertos.
Una prueba más extrema sería eliminar ese índice por completo al INSERTAR y luego volver a crearlo cuando haya terminado.
fuente
MySQL no solo bloquea la fila afectada, sino también la fila de índice afectada y el espacio entre las filas de índice (como se describe aquí ). Dado que las claves primarias siempre están indexadas y las usa en sus actualizaciones, sospecho que las transacciones múltiples que intentan actualizar varias filas resultan en bloqueos de brecha de índice superpuestos que a su vez crean el punto muerto.
Para resolver esto, también recomiendo el consejo de Oreos para mantener una transacción lo más pequeña posible. Si las filas actualizadas son independientes entre sí, debe usar una transacción separada para cada una de ellas.
fuente