Causas y soluciones del interbloqueo de MySQL

prefacio

Recientemente, Lao Gu se encontró a menudo con colegas que decían que mysql estaba bloqueado nuevamente, lo que provocaba errores comerciales . Hoy hablaremos del interbloqueo y como solucionarlo

Tipo de bloqueo

nivel de bloqueo mysql: nivel de página, nivel de tabla, nivel de fila

Bloqueos a nivel de tabla: sobrecarga baja, bloqueo rápido, sin interbloqueos, gran granularidad de bloqueo, mayor probabilidad de conflictos de bloqueo y menor concurrencia.

Bloqueos a nivel de fila: alta sobrecarga, bloqueo lento; pueden ocurrir interbloqueos; la granularidad de bloqueo más pequeña, la probabilidad más baja de conflictos de bloqueo y la concurrencia más alta.

Bloqueo de página: la sobrecarga y el tiempo de bloqueo están entre los bloqueos de tabla y los bloqueos de fila; se producirán interbloqueos; la granularidad de bloqueo está entre los bloqueos de tabla y los bloqueos de fila, y la simultaneidad es promedio

Causas y ejemplos de interbloqueo

1. Causas:

Deadlock se refiere a un fenómeno en el que dos o más procesos esperan el uno al otro debido a la competencia por los recursos durante la ejecución . Si no hay una fuerza externa, no podrán avanzar. En este momento, se dice que el sistema está en un estado de interbloqueo O el sistema tiene un interbloqueo, y estos procesos que siempre están esperando el uno al otro se denominan procesos de interbloqueo . Los bloqueos a nivel de tabla no causarán interbloqueos, por lo tanto, la solución a los interbloqueos es principalmente para el InnoDB más utilizado.

La clave del interbloqueo es que el orden en que se bloquean dos (o más) sesiones es inconsistente.

Entonces, la clave para resolver el problema del interbloqueo es dejar que diferentes sesiones se bloqueen en orden.

2. Genera un ejemplo:

Caso número uno

Requisito: Divida el dinero de la inversión en varias acciones y distribúyalas al azar entre los prestatarios.

En un principio, la idea del programa empresarial era la siguiente:

Después de que el inversionista invierte, la cantidad se divide aleatoriamente en varias partes, y luego se seleccionan aleatoriamente algunas de la tabla de prestatarios, y luego el saldo en la tabla de prestatarios se actualiza a través de la selección para actualizar uno por uno.

Por ejemplo, dos usuarios invierten al mismo tiempo, y la cantidad del usuario A se divide aleatoriamente en 2 acciones, que se distribuyen a los prestatarios 1 y 2.

La cantidad del usuario B se divide aleatoriamente en 2 acciones, distribuidas a los prestatarios 2, 1

Debido al diferente orden de bloqueo, por supuesto, pronto aparecerá un interbloqueo.

La mejora de este problema es muy simple, simplemente bloquee todos los prestatarios asignados a la vez.

Select * from xxx where id in (xx,xx,xx) for update

Los valores de la lista mysql en se ordenarán automáticamente de menor a mayor, y los bloqueos se agregarán uno por uno de menor a mayor

primera sesión:

Nota: desactive el envío automático set autocommit=0;

mysql> select * from goods where goods_id in (2,3) for update;
+----+--------+------+---------------------+
| good_id | goods_name | price             |
+----+--------+------+---------------------+
|  2 | bbbb     | 1.00 |
|  3 | vvv     | 3.00 |
+----+--------+------+---------------------+

Segunda sesión:

select * from goods where goods_id in (3,4,5) for update;

candado esperando...

caso dos

En desarrollo, se suele hacer este tipo de requisito de juicio: consultar según el valor del campo (con índice), si no existe, insertarlo; de lo contrario, actualizar.

Tomando id como la clave principal como ejemplo, todavía no hay una fila con id = 22

Nota: desactive el envío automático set autocommit=0;

primera sesión:

select * from goods where goods_id=22 for update;

Segunda sesión:

select * from goods where goods_id=23  for update;

Y luego en la primera sesión:

insert into goods values(22,'ac',11.5);

candado esperando...

Luego a la segunda sesión:

insert into goods values(23,'bc',23.0);

ERROR 1213 (40001): Interbloqueo encontrado al intentar obtener el bloqueo; intente reiniciar la transacción

Al bloquear filas existentes (clave principal), mysql solo tiene bloqueos de fila.

Al bloquear una fila que no existe (incluso si la condición es la clave principal), mysql bloqueará un rango

El rango bloqueado es:

(infinitamente pequeño o menor que el valor máximo de la identificación bloqueada en la tabla, infinito o mayor que el valor mínimo de la identificación bloqueada en la tabla)

Por ejemplo: si hay una identificación existente en la tabla (11, 12)

luego bloquear (12, infinito)

Ejemplo 2: Si el id existente en la tabla es (11, 30)

Luego bloquear (11, 30)

La solución a este callejón sin salida es:

insert into goods(xx,xx) on duplicate key update `xx`='XX';

Use la sintaxis específica de mysql para resolver este problema. Debido a que la declaración de inserción es para la clave principal, no importa si la fila insertada existe o no, solo habrá bloqueos de fila.

Procesamiento de verificación de punto muerto

Normalmente, cuando ocurre un interbloqueo, la conexión con el menor peso se eliminará y se revertirá. Pero para encontrar la declaración para optimizar, habilite el interbloqueo para registrar la información del interbloqueo.

#step 1:窗口一
mysql> start transaction;
mysql> update aa set name='aaa' where id = 1;
 
#step 2:窗口二
mysql> start transaction;
mysql> update bb set name='bbb' where id = 1;
 
#step 3:窗口一
mysql> update bb set name='bbb';

Vista por

#step 4:窗口三
#是否自动提交
mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | ON    |
+---------------+-------+

ver enlace actual

#查看当前连接
mysql> show processlist;
mysql> show full processlist;
mysql> SELECT * FROM INFORMATION_SCHEMA.PROCESSLIST;
+----+------+-----------+------+---------+------+-------+------------------+
| Id | User | Host      | db   | Command | Time | State | Info             |
+----+------+-----------+------+---------+------+-------+------------------+
|  4 | root | localhost | test | Sleep   |  244 |       | NULL             |
|  5 | root | localhost | test | Sleep   |  111 |       | NULL             |
|  6 | root | localhost | NULL | Query   |    0 | init  | show processlist |
+----+------+-----------+------+---------+------+-------+------------------+
  • columna de identificación: un identificador, útil cuando desea eliminar una declaración.
  • columna de usuario: muestra el usuario actual, si no es root, este comando solo mostrará las declaraciones sql dentro de su autoridad.
  • Columna Host: Muestra desde qué ip y desde qué puerto se emite esta declaración. Se puede utilizar para rastrear al usuario de la declaración problemática.
  • Columna db: muestra a qué base de datos está conectado actualmente el proceso.
  • Columna de comandos: muestra los comandos ejecutados de la conexión actual, generalmente dormir, consultar y conectar
  • columna de tiempo: la duración de este estado, en segundos.

Lo más importante en este comando es la columna de estado.Los estados listados por mysql incluyen principalmente lo siguiente:

Mesa de control

Comprobación de la hoja de datos (esto es automático).

Mesas de cierre

Los datos modificados en la tabla se vacían en el disco y la tabla agotada se cierra. Esta es una operación rápida, de lo contrario, debe verificar si el espacio en disco está lleno o si el disco tiene una gran carga.

Conectar

El esclavo de replicación se está conectando al maestro.

Copiando a la tabla tmp en el disco

Dado que el conjunto de resultados temporal es más grande que tmp_table_size, la tabla temporal se convierte del almacenamiento en memoria al almacenamiento en disco para ahorrar memoria.

Creando la tabla tmp

Creación de una tabla temporal para contener algunos resultados de consulta.

borrando de la tabla principal

El servidor está realizando la primera parte de una eliminación de varias tablas y la primera tabla acaba de eliminarse.

eliminar de las tablas de referencia

El servidor está realizando la segunda parte de la eliminación de tablas múltiples, eliminando registros de otras tablas.

Mesas de lavado

FLUSH TABLES se está ejecutando, esperando que otros hilos cierren la tabla de datos.

Delicado

Envíe una solicitud de eliminación a un hilo, luego este hilo verificará el indicador de eliminación y abandonará la siguiente solicitud de eliminación al mismo tiempo. MySQL verificará el indicador de eliminación en cada ciclo principal, pero en algunos casos el hilo puede morir después de un corto período de tiempo. Si el subproceso está bloqueado por otros subprocesos, la solicitud de eliminación tendrá efecto inmediatamente cuando se libere el bloqueo.

bloqueado

bloqueado por otras consultas.

Enviando datos

Los registros de la consulta SELECT se están procesando y los resultados se están enviando al cliente.

Clasificación por grupo

Clasificación para GROUP BY.

Clasificación por orden

La clasificación se está realizando para ORDER BY.

Apertura de mesas

Este proceso debe ser rápido, salvo que interfieran otros factores. Por ejemplo, otros subprocesos no pueden abrir la tabla de datos hasta que se ejecute la instrucción ALTER TABLE o LOCK TABLE. Intentando abrir una mesa.

Eliminar duplicados

Se está ejecutando una consulta SELECT DISTINCT, pero MySQL no puede optimizar esos registros duplicados en la etapa anterior. Por lo tanto, MySQL necesita eliminar los registros duplicados nuevamente y luego enviar los resultados al cliente.

Reabrir mesa

Se adquiere un bloqueo en una tabla, pero el bloqueo debe adquirirse después de que se haya modificado la estructura de la tabla. Se ha liberado el bloqueo, la tabla de datos está cerrada y la tabla de datos está intentando volver a abrirse.

Reparación por clasificación

Las directivas de reparación se clasifican para crear índices.

Reparar con keycache

La instrucción de reparación utiliza el caché de índice para crear nuevos índices uno por uno. Será más lento que Reparar ordenando.

Buscando filas para actualizar

Conocer los registros que cumplen las condiciones para su actualización. Debe hacerse antes de que UPDATE modifique los registros relacionados.

Durmiendo

Esperando a que el cliente envíe una nueva solicitud.

Bloqueo del sistema

Esperando para adquirir un bloqueo del sistema externo. Si actualmente no está ejecutando varios servidores mysqld solicitando la misma tabla al mismo tiempo, puede deshabilitar los bloqueos del sistema externo agregando el parámetro --skip-external-locking.

Actualización de bloqueo

INSERT DELAYED está intentando adquirir una tabla de bloqueo para insertar nuevos registros.

Actualizando

Se buscan registros coincidentes y se modifican.

Bloqueo de usuario

Esperando GET_LOCK().

esperando mesas

Se notifica al subproceso que la estructura de la tabla de datos se ha modificado y la tabla de datos debe volver a abrirse para obtener la nueva estructura. Luego, para poder reabrir la tabla de datos, debe esperar hasta que todos los demás subprocesos cierren la tabla. Esta notificación se genera en los siguientes casos: FLUSH TABLES tbl_name, ALTER TABLE, RENAME TABLE, REPAIR TABLE, ANALYZE TABLE u OPTIMIZE TABLE.

Esperando la inserción del controlador

INSERT DELAYED ha procesado todas las operaciones de inserción pendientes y está esperando nuevas solicitudes.

Ver transacciones actualmente bloqueadas

 
#查看当前正在被锁的事务(锁请求超时后则查不到)
mysql> SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;
+------------------+-------------+-----------+-----------+-------------+-----------------+------------+-----------+----------+----------------+
| lock_id          | lock_trx_id | lock_mode | lock_type | lock_table  | lock_index      | lock_space | lock_page | lock_rec | lock_data      |
+------------------+-------------+-----------+-----------+-------------+-----------------+------------+-----------+----------+----------------+
| 130718495:65:3:4 | 130718495   | X         | RECORD    | `test`.`bb` | GEN_CLUST_INDEX |         65 |         3 |        4 | 0x000000000300 |
| 130718496:65:3:4 | 130718496   | X         | RECORD    | `test`.`bb` | GEN_CLUST_INDEX |         65 |         3 |        4 | 0x000000000300 |
+------------------+-------------+-----------+-----------+-------------+-----------------+------------+-----------+----------+----------------+

Ver transacciones actualmente en espera de bloqueos

#查看当前等待锁的事务(锁请求超时后则查不到)
mysql> SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS; 
+-------------------+-------------------+-----------------+------------------+
| requesting_trx_id | requested_lock_id | blocking_trx_id | blocking_lock_id |
+-------------------+-------------------+-----------------+------------------+
| 130718499         | 130718499:65:3:4  | 130718500       | 130718500:65:3:4 |
+-------------------+-------------------+-----------------+------------------+

Ver transacciones actualmente no comprometidas

#查看当前未提交的事务(如果死锁等待超时,事务可能还没有关闭)
mysql> SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX;

Mire principalmente los campos señalados por las flechas.Si hay datos bloqueados (si no es 0, está bloqueado), después de encontrarlos, de acuerdo con el campo en la figura a continuación: try_mysql_thread_id se usa como la identificación de la clave principal de este data para ejecutar este sql para eliminar: kill id; (Elimine el proceso correspondiente a la id) Suponga que los datos de try_mysql_thread_id=14 están bloqueados. Si ejecutamos kill 14 para eliminar, la tabla ya no estará bloqueada.

Ver la tabla a la que se accede

#查看正在被访问的表
mysql> show OPEN TABLES where In_use > 0;
+----------+-------+--------+-------------+
| Database | Table | In_use | Name_locked |
+----------+-------+--------+-------------+
| test     | bb    |      1 |           0 |
+----------+-------+--------+-------------+

excepción de interbloqueo

#step 3:窗口一 (若第三步中锁请求太久,则出现锁超时而终止执行)
mysql> update bb set name='bbb';
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
 
 
#"窗口一" 锁请求超时前,执行第五步,使死锁产生,则该连接 "窗口二" 执行终止,"窗口一" 顺利执行
#step 5:窗口二
mysql> update aa set name='aa';
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

Ver la última situación de interbloqueo

mysql> SHOW ENGINE INNODB STATUS;

Configuración de parámetros relacionados

registro de punto muerto

#死锁记录只记录最近一个死锁信息,若要将每个死锁信息都保存到错误日志,启用以下参数:
mysql> show variables like 'innodb_print_all_deadlocks';
+----------------------------+-------+
| Variable_name              | Value |
+----------------------------+-------+
| innodb_print_all_deadlocks | OFF   |
+----------------------------+-------+

tiempo de espera de bloqueo

 
#上面 【step 3:窗口一】若一直请求不到资源,默认50秒则出现锁等待超时。
mysql> show variables like 'innodb_lock_wait_timeout'; 
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| innodb_lock_wait_timeout | 50    |
+--------------------------+-------+
 
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
 
 
#设置全局变量 锁等待超时为60秒(新的连接生效)
#mysql> set session innodb_lock_wait_timeout=50; 
mysql> set global innodb_lock_wait_timeout=60; 

reversión de transacciones

 
#上面测试中,当事务中的某个语句超时只回滚该语句,事务的完整性属于被破坏了。为了回滚这个事务,启用以下参数:
mysql> show variables like 'innodb_rollback_on_timeout';
+----------------------------+-------+
| Variable_name              | Value |
+----------------------------+-------+
| innodb_rollback_on_timeout | OFF   |
+----------------------------+-------+

La configuración final de los parámetros es la siguiente: (reiniciar la prueba de reconexión del servicio)

[mysqld]
log-error =/var/log/mysqld3306.log
innodb_lock_wait_timeout=60     #锁请求超时时间()
innodb_rollback_on_timeout = 1  #事务中某个语句锁请求超时将回滚真个事务
innodb_print_all_deadlocks = 1  #死锁都保存到错误日志

Comando='Dormir'

Indica que la conexión está inactiva, si hay demasiadas, puede eliminarla manualmente

#若手动删除堵塞会话,删除 Command='Sleep' 、无State、无Info、trx_weight 权重最小的。
show processlist;
SELECT trx_mysql_thread_id,trx_state,trx_started,trx_weight FROM INFORMATION_SCHEMA.INNODB_TRX;

Supongo que te gusta

Origin blog.csdn.net/qq_43842093/article/details/132009933
Recomendado
Clasificación