Caso cinco de interbloqueo

Fuente: cuenta pública yangyidba


I. Introducción

El punto muerto es en realidad un problema técnico muy interesante y desafiante, probablemente todos los administradores de bases de datos y algunos desarrolladores lo encontrarán en el proceso de 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 se deriva de un caso de punto muerto en el proceso de producción.

2. Conocimientos previos

El documento oficial [1] decía:

"REEMPLAZAR se realiza como un INSERTAR si no hay colisión en una llave única. De lo contrario, se coloca un bloqueo exclusivo de la siguiente tecla en la fila que se reemplazará".

"Si no hay un conflicto de clave única, la operación de reemplazo y el bloqueo de inserción son iguales. Pero si hay un conflicto de clave única, el sistema agregará el bloqueo de la siguiente clave LOCK X al registro cuando se ejecute la instrucción de reemplazo".

Si cree que la traducción anterior es relativamente simple, solo eche un vistazo a la siguiente introducción [2]

create table t1(
a int auto_increment primary key,
b int,
c int,
unique key (b));
replace into t1(b,c) values (2,3)

Paso 1 Lógica de inserción normal

Primero, inserte el índice agrupado. En el ejemplo anterior, la columna a es una columna de incremento automático. Dado que no se especifica explícitamente, se generará un nuevo valor no conflictivo antes de cada inserción.

Luego inserte el índice secundario b, porque es el único índice, al verificar la clave duplicada, agregue el bloqueo de registro, el tipo es LOCK_X

Para operaciones INSERT normales, cuando es necesario comprobar la clave duplicada, agregue el bloqueo LOCK_S, y para las operaciones Reemplazar en o INSERT..ON DUPLICATE, agregue el bloqueo de registro LOCK_X. Cuando el registro ya existe, se devuelve el error DB_DUPLICATE_KEY.

Paso 2 manejar errores

Dado que la clave duplicada se detectó en el paso anterior, el registro de índice agrupado insertado en el primer paso debe revertirse.

Paso 3 Operación de conversión

Después de no poder regresar de la capa InnoDB a la capa del servidor y recibir un error de clave duplicada, primero recupere el índice con el conflicto de clave única y bloquee el registro de índice en conflicto (y el registro de índice agrupado)

Luego confirme el modo de conversión para resolver el conflicto:

Si el índice en conflicto del Reino Unido es el último índice único, no hay una referencia de clave externa y no hay un activador de eliminación, use UPDATE ROW para resolver el conflicto

De lo contrario, utilice BORRAR FILA + INSERTAR FILA para resolver el conflicto. Si se trata de un conflicto de clave principal, primero se eliminará.

Paso 4 Actualizar registros

En este ejemplo, a es la clave principal. Las actualizaciones del índice agrupado y el índice secundario se marcan como eliminación + inserción de nuevos registros. Para índices agrupados, debido a cambios en la columna PK, utilice eliminar + insertar registros de índice agrupados para actualizar. Para el índice de clave única secundaria, se adopta la misma forma de marcar eliminación + inserción.

Tres, análisis de casos

3.1 Prepare el entorno de prueba

Nivel de aislamiento de transacciones REPEATABLE READ

preparación de datos

create table ix(id int not null auto_increment,
a int not null ,
b int not null ,
primary key(id),
idxa(a)
) engine=innodb default charset=utf8;
insert into ix(a,b) valuses(1,1),(5,10),(15,12);

Escenario de punto muerto

3.2 Análisis de procesos

Después de ejecutar una declaración cada vez, ejecute show innodb engine status para ver el estado de la transacción,

El registro de transacciones para ejecutar el reemplazo en valores ix (a, b) (5,8) es el siguiente

---TRANSACTION 1872, ACTIVE 46 sec
4 lock struct(s), heap size 1136, 4 row lock(s), undo log entries 2
MySQL thread id 1156, OS thread handle 0672, query id 114 localhost msandbox

análisis

reemplace en ix (a, b) valores (5,8), debido a que el registro a = 5 ya existe, el registro se actualizará y el registro se agregará con Bloqueo de tecla Siguiente Bloqueo de registro, Bloqueo de espacios,

Esta transacción genera 2 deshacer, con 4 cerraduras y una cerradura IX, cerradura de 1 fila para a = 5 filas y 2 cerraduras de espacio entre 1-5 y 5-15.

El registro de transacciones para ejecutar el reemplazo en valores ix (a, b) (8,10) es el siguiente

---TRANSACTION 1873, ACTIVE 3 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s),
undo log entries 1
MySQL thread id 1155, OS thread handle 3008,
query id 117 localhost msandbox update
replace into ix(a,b) values(8,10)
------- TRX HAS BEEN WAITING 3 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 24 page no 4 n bits 80 
index idx_a of table `test`.`ix` trx id 1873 
lock_mode X locks gap before rec insert intention waiting
---TRANSACTION 1872, ACTIVE 69 sec
4 lock struct(s), heap size 1136, 4 row lock(s), undo log entries 2

análisis

No hay registro de a = 8 en la tabla, por lo que es similar a insertar en ix (a, b) valores (8,10). Pero a = 8 entra en conflicto con el bloqueo de espacio [5-15] mantenido por sess1, por lo que espera a que lock_mode X bloquee el espacio antes de rec insertar intención en espera y entra en la cola de espera. Este bloqueo lo mantiene sess1.

Ejecute reemplazar en valores ix (a, b) (9,12); el registro de transacciones ejecuta la declaración de la siguiente manera : sess2 informa inmediatamente un punto muerto

*** (1) TRANSACTION:
TRANSACTION 1866, ACTIVE 8 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 1155, OS thread handle 3008, query id 101 localhost msandbox update
replace into ix(a,b) values(8,10)
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 24 page no 4 n bits 80 index idx_a of table `test`.`ix` trx id 1866 
lock_mode X locks gap before rec insert intention waiting
*** (2) TRANSACTION:
TRANSACTION 1865, ACTIVE 19 sec inserting
mysql tables in use 1, locked 1
5 lock struct(s), heap size 1136, 5 row lock(s),
undo log entries 3
MySQL thread id 1156, OS thread handle 0672,
query id 102 localhost msandbox update
replace into ix(a,b) values(9,12)
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 24 page no 4 n bits 80 index idx_a of table `test`.`ix` trx id 1865 lock_mode X
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 24 page no 4 n bits 80 
index idx_a of table `test`.`ix` trx id 1865 
lock_mode X locks gap before rec insert intention waiting
*** WE ROLL BACK TRANSACTION (1)

Análisis de registros

  1. reemplace en ix (a, b) valores (9,12); Similar a insert (8,10), debe solicitar lock_mode X bloquea el espacio antes de rec insertar la intención en espera, e ingresar a la cola de solicitud de bloqueo para esperar.

  2. La transacción T2 se reemplaza en valores ix (a, b) (5,8); Esta declaración contiene 4 bloqueos y un bloqueo IX, 1 bloqueo de fila para a = 5 filas y 2 a en 1-5,5- Bloqueo de GAP entre 15.

  3. La transacción T1 se reemplaza en los valores ix (a, b) (8,10); a = 8 entra en conflicto con el bloqueo de espacio [5,15] mantenido por sess1, por lo que espera a lock_mode X bloquea el espacio antes de rec insertar la intención en espera y entra en la cola de espera dentro.

  4. La transacción T2 se reemplaza en los valores ix (a, b) (9,12), a = 9 también está entre [5-15], es necesario esperar a que se libere el bloqueo de intención de inserción de T1, T1 espera a T2 (SQL1), T2 (SQL2) A la espera de que T1 dé lugar a un punto muerto, el sistema elige revertir la transacción T1.

Cuatro, resumen

Análisis y posicionamiento del problema, ¿cómo solucionarlo? El consejo actual para el desarrollo es evitar usar el método reemplazar en, usar un método de selección + verificación + inserción, o si se puede aceptar un cierto punto muerto, puede reducir la ejecución concurrente y el cambio a serie. Los amigos interesados ​​pueden reproducirse, tener mejores soluciones y comunicarse entre sí.

Cinco, referencia

[1] https://dev.mysql.com/doc/refman/5.7/en/innodb-locks-set.html explica cómo bloquear varias declaraciones. Los estudiantes que estén interesados ​​en el punto muerto no deben perderse.

[2] http://mysqllover.com/?p=1312

Este artículo fue transferido de la cuenta pública del maestro Yang Qilong (yangyidba). Se ha centrado en la tecnología de bases de datos y la optimización del rendimiento, el análisis de casos de fallas, el intercambio de conocimientos técnicos de operación y mantenimiento de bases de datos, el crecimiento personal y la autogestión y otros temas durante mucho tiempo. Bienvenido a escanear el código para llamar la atención.

Lectura extendida

El texto completo ha terminado.

Disfruta MySQL :)

El nuevo curso K8S de Zhishutang está en línea

Escanee el código para comenzar un nuevo viaje de aprendizaje

Supongo que te gusta

Origin blog.csdn.net/n88Lpo/article/details/108655883
Recomendado
Clasificación