Fuente: cuenta pública yangyidba
Un
punto muerto , prólogo , de hecho, es un problema técnico muy interesante también es muy desafiante, probablemente cada parte del desarrollo y los estudiantes de DBA se encontrarán en el curso de su trabajo. Con respecto al punto muerto, continuaré escribiendo una serie de estudios de casos, con la esperanza de ayudar a los amigos que quieran entender el punto muerto. Este artículo presenta un ejemplo de interbloqueo causado por tres insertos simultáneos. La causa principal es que la clave única del inserto se aplica al bloqueo GAP especial del bloqueo de intención de inserción. De hecho, es más razonable llamar a Insertar Intention Gap Lock.
2. Análisis de casos
2.1 Preparación del entorno
Servidor Percona 5.6 Modo RR
CREAR TABLA `t6` (
ʻId` int (11) NOT NULL AUTO_INCREMENT,
ʻA` int (11) DEFAULT NULL,
CLAVE PRIMARIA (ʻid`),
CLAVE única ʻidx_a` (ʻa`)
) MOTOR = InnoDB AUTO_INCREMENT = 1 JUEGO DE CARTAS POR DEFECTO = utf8mb4;
insertar en los valores t6 (1,2), (2,8), (3,9), (4,11), (5,19)
sess1 |
sess2 |
sess3 |
empezar; |
||
insertar en t6 (id, a) valores (6,15); |
empezar; |
|
insertar en t6 (id, a) valores (7,15); |
empezar; |
|
insertar en t6 (id, a) valores (8,15); |
||
Retroceder; |
ERROR 1213 (40001): Se ha encontrado un interbloqueo al intentar obtener el bloqueo; intente reiniciar la transacción |
2.2 Registro de interbloqueo
------------------------
ÚLTIMO DEADLOCK DETECTADO
------------------------
2017-09-18 10:03:50 7f78eae30700
*** (1) TRANSACCIÓN:
TRANSACCIÓN 462308725, ACTIVO 18 segundos insertando, hilo declarado dentro de InnoDB 1
tablas mysql en uso 1, bloqueadas 1
LOCK WAIT 4 estructura (s) de bloqueo, tamaño de pila 1184, bloqueo (s) de 2 filas, deshacer entradas de registro 1
Id. De subproceso de MySQL 3825465, identificador de subproceso del sistema operativo 0x7f78eaef4700, Id. De consulta 781148519 actualización de raíz localhost
insertar en t6 (id, a) valores (7,15)
*** (1) ESPERANDO A QUE SE OTORGUE ESTE BLOQUEO:
RECORD LOCKS espacio id 227 página no 4 n bits 80 índice ʻidx_a` de la tabla `test``t6` trx id 462308725 lock_mode X insertar intención en espera
*** (2) TRANSACCIÓN:
TRANSACCIÓN 462308726, ACTIVO 10 segundos insertando, hilo declarado dentro de InnoDB 1
tablas mysql en uso 1, bloqueadas 1
4 estructuras de bloqueo, tamaño de pila 1184, bloqueo (s) de 2 filas, deshacer entradas de registro 1
Id. De subproceso de MySQL 3825581, identificador de subproceso del sistema operativo 0x7f78eae30700, Id. De consulta 781148528 actualización de raíz localhost
insertar en t6 (id, a) valores (8,15)
*** (2) SOSTIENE EL (LOS) BLOQUEO (S):
RECORD LOCKS espacio id 227 página no 4 n bits 80 índice ʻidx_a` de la tabla `test``t6` trx id 462308726 modo de bloqueo S
*** (2) ESPERANDO A QUE SE OTORGUE ESTE BLOQUEO:
RECORD LOCKS espacio id 227 página no 4 n bits 80 índice ʻidx_a` de la tabla `test``t6` trx id 462308726 lock_mode X insertar intención en espera
*** RETROCAMOS LA TRANSACCIÓN (2)
2.3 Análisis de interbloqueo En
primer lugar, aún necesitamos enfatizar nuevamente la lógica de bloqueo de la operación de inserción.
La primera etapa: verificación de restricción de unicidad, primero solicite LOCK_S + LOCK_ORDINARY.
Segunda etapa: adquiera el bloqueo de la etapa uno e inserte correctamente, la posición de inserción tiene un bloqueo de espacio: LOCK_INSERT_INTENTION, para evitar otros conflictos de claves únicas de inserción.
Nueva inserción de datos: LOCK_X + LOCK_REC_NOT_GAP
Para la operación de inserción, si se produce un conflicto de restricción única, debe agregar S Bloqueo de tecla siguiente al índice único en conflicto. A partir de aquí, encontrará que incluso en el nivel de aislamiento de la transacción RC, también habrá un bloqueo de bloqueo de tecla siguiente, bloqueando así la concurrencia. Sin embargo, lo que el documento no explica es que para el índice único donde se detecta el conflicto, el hilo de espera necesita bloquear el siguiente registro después de obtener el Bloqueo S. El código fuente se juzga mediante la función row_ins_scan_sec_index_for_duplicate.
En segundo lugar, necesitamos la matriz de compatibilidad desbloqueada.
De la matriz de compatibilidad, podemos sacar las siguientes conclusiones:
No habrá conflictos entre las operaciones INSERT.
GAP, Next-Key bloqueará Insert.
GAP y Record, Next-Key no entrarán en conflicto
Grabar y grabar, la siguiente clave entra en conflicto entre sí.
El bloqueo de inserción existente no impide que se agreguen bloqueos.
En este caso, se ejecutan tres sesiones al mismo tiempo, planeo analizar el registro de transacciones después de que cada paso se ejecuta paso a paso.
El primer paso, sess1 realiza la operación de inserción
insertar en t6 (id, a) valores (6,15);
--- TRANSACCIÓN 462308737, ACTIVO 5 seg
1 estructura (s) de bloqueo, tamaño de pila 360, 0 bloqueo (s) de fila, deshacer entradas de registro 1
Id. De subproceso de MySQL 3825779, identificador de subproceso del sistema operativo 0x7f78eacd9700, Id. De consulta 781149440 localhost root init
mostrar el estado del innodb del motor
TABLE LOCK table `test``t6` trx id 462308737 modo de bloqueo IX
Debido a la primera declaración insertada, la verificación de conflicto de unicidad pasó y la inserción fue exitosa (6,15). En este momento, la sesión sess1 mantiene el bloqueo LOCK_X | LOCK_REC_NOT_GAP de (6,15). Consulte "INSERT establece un bloqueo exclusivo en la fila insertada. Este bloqueo es un bloqueo de registro de índice, no un bloqueo de tecla siguiente (es decir, no hay bloqueo de espacio) y no evita que otras sesiones se inserten en el espacio antes de la fila insertada. "El
segundo paso, sess2 realiza una operación de
inserción inserta en los valores t6 (id, a) (7,15);
--- TRANSACCIÓN 462308738, ACTIVO 4 seg insertando
tablas mysql en uso 1, bloqueadas 1
LOCK WAIT 2 estructura (s) de bloqueo, tamaño de pila 360, 1 bloqueo (s) de fila, deshacer entradas de registro 1
Id. De subproceso de MySQL 3825768, identificador de subproceso del sistema operativo 0x7f78ea9c9700, Id. De consulta 781149521 actualización de raíz localhost
insertar en t6 (id, a) valores (7,15)
------- TRX HA ESTADO ESPERANDO 4 SEGUNDOS PARA QUE SE OTORGUE ESTE BLOQUEO:
RECORD LOCKS espacio id 227 página no 4 n bits 80 índice ʻidx_a` de la tabla `test``t6` id trx 462308738 modo de bloqueo S en espera
------------------
TABLE LOCK table `test``t6` trx id 462308738 modo de bloqueo IX
RECORD LOCKS espacio id 227 página no 4 n bits 80 índice ʻidx_a` de la tabla `test``t6` id trx 462308738 modo de bloqueo S en espera
--- TRANSACCIÓN 462308737, ACTIVO 66 seg.
2 estructura (s) de bloqueo, tamaño de pila 360, 1 bloqueo (s) de fila, deshacer entradas de registro 1
Id. De subproceso de MySQL 3825779, identificador de subproceso del sistema operativo 0x7f78eacd9700, Id. De consulta 781149526 localhost root init
mostrar el estado del innodb del motor
TABLE LOCK table `test``t6` trx id 462308737 modo de bloqueo IX
RECORD LOCKS espacio id 227 página no 4 n bits 80 índice ʻidx_a` de la tabla `test``t6` id trx 462308737 lock_mode X bloquea rec pero no gap
Primero, la inserción de sess2 se aplica para un bloqueo IX, porque la sesión sess1 se ha insertado con éxito y mantiene el bloqueo de la fila X con la clave única a = 15, por lo que la inserción de sess2 realiza la verificación de unicidad, primero solicita LOCK_S + LOCK_ORDINARY y la lista de registro de transacciones solicita el modo de bloqueo S esperando
El tercer paso, sess3 realiza una operación de
inserción insertar en los valores t6 (id, a) (8,15);
--- TRANSACCIÓN 462308739, ACTIVO 3 segundos insertando
tablas mysql en uso 1, bloqueadas 1
LOCK WAIT 2 estructura (s) de bloqueo, tamaño de pila 360, 1 bloqueo (s) de fila, deshacer entradas de registro 1
Id. De subproceso de MySQL 3825764, identificador de subproceso del sistema operativo 0x7f78ea593700, Id. De consulta 781149555 actualización de raíz localhost
insertar en t6 (id, a) valores (8,15)
------- TRX HA ESTADO ESPERANDO 3 SEGUNDOS PARA QUE SE OTORGUE ESTE BLOQUEO:
RECORD LOCKS espacio id 227 página no 4 n bits 80 índice ʻidx_a` de la tabla `test``t6` id trx 462308739 modo de bloqueo S en espera
------------------
TABLE LOCK table `test``t6` trx id 462308739 modo de bloqueo IX
RECORD LOCKS espacio id 227 página no 4 n bits 80 índice ʻidx_a` de la tabla `test``t6` id trx 462308739 modo de bloqueo S en espera
--- TRANSACCIÓN 462308738, ACTIVO 35 segundos insertando
tablas mysql en uso 1, bloqueadas 1
LOCK WAIT 2 estructura (s) de bloqueo, tamaño de pila 360, 1 bloqueo (s) de fila, deshacer entradas de registro 1
Id. De subproceso de MySQL 3825768, identificador de subproceso del sistema operativo 0x7f78ea9c9700, Id. De consulta 781149521 actualización de raíz localhost
insertar en t6 (id, a) valores (7,15)
------- TRX HA ESTADO ESPERANDO 35 SEGUNDOS PARA QUE SE OTORGUE ESTE BLOQUEO:
RECORD LOCKS espacio id 227 página no 4 n bits 80 índice ʻidx_a` de la tabla `test``t6` id trx 462308738 modo de bloqueo S en espera
------------------
TABLE LOCK table `test``t6` trx id 462308738 modo de bloqueo IX
RECORD LOCKS espacio id 227 página no 4 n bits 80 índice ʻidx_a` de la tabla `test``t6` id trx 462308738 modo de bloqueo S en espera
--- TRANSACCIÓN 462308737, ACTIVO 97 seg.
2 estructura (s) de bloqueo, tamaño de pila 360, 1 bloqueo (s) de fila, deshacer entradas de registro 1
Id. De subproceso de MySQL 3825779, identificador de subproceso del sistema operativo 0x7f78eacd9700, Id. De consulta 781149560 localhost root init
mostrar el estado del innodb del motor
TABLE LOCK table `test``t6` trx id 462308737 modo de bloqueo IX
RECORD LOCKS espacio id 227 página no 4 n bits 80 índice ʻidx_a` de la tabla `test``t6` id trx 462308737 lock_mode X bloquea rec pero no gap
Es consistente con el proceso de aplicación de bloqueo de la sesión sess2, todos los cuales están esperando que sess1 libere los recursos de bloqueo.
El cuarto paso sess1 realiza una operación de reversión, sess2 no confirma
sess1 rollback;
En este momento, sess2 se insertó correctamente y sess3 está en punto muerto. En este momento, el inserto de sess2 se insertó correctamente y aún no se ha enviado. La lista de transacciones es la siguiente:
------------
ACTAS
------------
Contador de id trx 462308744
Purga realizada para trx sn: o <462308744 deshacer n: o <0 estado: en ejecución pero inactivo
Longitud de la lista de historia 1866
LISTA DE TRANSACCIONES PARA CADA SESIÓN:
--- TRANSACCIÓN 462308737, no iniciada
Id. De subproceso de MySQL 3825779, identificador de subproceso del sistema operativo 0x7f78eacd9700, Id. De consulta 781149626 localhost root init
mostrar el estado del innodb del motor
--- TRANSACCIÓN 462308739, no iniciada
Id. De subproceso de MySQL 3825764, identificador de subproceso del sistema operativo 0x7f78ea593700, Id. De consulta 781149555 limpieza de raíz localhost
--- TRANSACCIÓN 462308738, ACTIVO 75 seg.
5 estructura (s) de bloqueo, tamaño de pila 1184, bloqueo (s) de 3 filas, deshacer entradas de registro 1
Id. De subproceso de MySQL 3825768, identificador de subproceso del sistema operativo 0x7f78eadce700, Id. De consulta 781149608 limpieza de raíz localhost
TABLE LOCK table `test``t6` trx id 462308738 modo de bloqueo IX
RECORD LOCKS espacio id 227 página no 4 n bits 80 índice ʻidx_a` de la tabla `test``t6` trx id 462308738 modo de bloqueo S
RECORD LOCKS espacio id 227 página no 4 n bits 80 índice ʻidx_a` de la tabla `test``t6` trx id 462308738 modo de bloqueo S
RECORD LOCKS espacio id 227 página no 4 n bits 80 índice ʻidx_a` de la tabla `test``t6` id trx 462308738 lock_mode X insertar intención
RECORD LOCKS espacio id 227 página no 4 n bits 80 índice ʻidx_a` de la tabla `test``t6` trx id 462308738 modo de bloqueo S bloquea el espacio antes de la grabación
Tres, la causa del estancamiento
La inserción de sess1 es exitosa y se agrega un bloqueo X a la clave única de a = 15.
sess2 ejecuta insert (6,15) y realiza una verificación de unicidad antes de insertar. Se encuentra que la clave duplicada del registro insertado de sess1 necesita solicitar LOCK_S | LOCK_ORDINARY, pero entra en conflicto con sess1 (LOCK_X | LOCK_REC_NOT_GAP), únete a la cola de espera y espera a sess1 Suelte el candado.
sess3 ejecuta insert (7,15) y realiza una verificación de unicidad antes de insertar. Se encuentra que la clave duplicada con el registro insertado de sess1 necesita solicitar LOCK_S | LOCK_ORDINARY, pero entra en conflicto con sess1 (LOCK_X | LOCK_REC_NOT_GAP), únete a la cola de espera y espera a sess1 Suelte el candado.
sess1 ejecuta una reversión, sess1 libera el bloqueo de registro exclusivo (LOCK_X | LOCK_REC_NOT_GAP) en el índice a = 15, y luego sess2 y sess3 obtienen el bloqueo S (LOCK_S | LOCK_ORDINARY) con éxito, sess2 y sess3 solicitan el bloqueo de registro exclusivo en el índice a = 15 (LOCK_X | LOCK_REC_NOT_GAP), el registro solicita lock_mode X insertar la intención. Debido a que el bloqueo X y el bloqueo S son mutuamente excluyentes, tanto sess2 como sess3 esperan que el otro libere el bloqueo S, por lo que se produce un interbloqueo y MySQL elige deshacer uno de ellos.
Cuatro, resumen
El análisis de interbloqueo ya es muy desafiante, especialmente para insertar conflictos de claves únicas, que deben aplicarse en múltiples etapas y comprender la matriz de compatibilidad de los bloqueos. Para este conocimiento que necesito aprender y comprender, este artículo se puede considerar como una introducción. Si hay algún análisis y comprensión incorrectos, corríjame.
Lectura extendida
El texto completo ha terminado.
Disfruta MySQL :)
La clase "MySQL Core Optimization" de Teacher Ye se ha actualizado a MySQL 8.0, escanee el código para comenzar el viaje de la práctica de MySQL 8.0