Bloqueos globales de MySQL y bloqueos a nivel de tabla

Prefacio

Cuando utilizo MySQL en un entorno de desarrollo empresarial real, definitivamente no solo yo uso MySQL, sino que un equipo usa explícitamente MySQL, o las empresas usan implícitamente MySQL, por lo que cuando varios usuarios o clientes se conectan y usan, debemos considerar una pregunta: cómo para garantizar la coherencia del acceso concurrente a los datos ? En este artículo, hablaré sobre los bloqueos de MySQL, sin involucrar el nivel de aislamiento de transacciones de MySQL.

Bloqueo global

El bloqueo global de MySQL cerrará todas las tablas abiertas y hará que todas las tablas estén en un estado de solo lectura. Sus comandos son:

# 全局锁,简称FTWRL
FLUSH TABLES WITH READ LOCK;

# 解锁命令
UNLOCK TABLES;

Experimente con FTWRL: (Todos los siguientes experimentos se realizan en MySQL8.0.22)

Sesión 1 sesión2
MESAS EMPOTRAR CON BLOQUEO DE LECTURA;
seleccione * del límite de prueba 1;
(devuelva los resultados normalmente)
seleccione * del límite de prueba 1;
(devuelva los resultados normalmente)
insertar en la prueba (a, b, c) valores (6,6,6);
(informe de error)
insertar en test (a, b, c) valores (8,8,8); # sql1
(bloqueo)
DESBLOQUEAR TABLAS;
insertar en los valores de prueba (a, b, c) (8,8,8); # sql1
(después de desbloquear session1, sql1 se ejecuta con éxito inmediatamente)

A partir del experimento anterior, se puede concluir que cuando se ejecuta FTWRL, todas las tablas pasan a ser de solo lectura y se bloquearán otras operaciones de actualización.

La función principal del bloqueo global es realizar una copia de seguridad lógica de toda la base de datos, es decir, seleccionar cada tabla de la base de datos y guardarla como texto.

Durante el proceso de copia de seguridad, toda la base de datos está en un estado de solo lectura y el riesgo es extremadamente alto. Si está respaldado en la biblioteca principal, hará que todas las tablas de negocios no puedan modificar los datos; si está respaldado en la biblioteca esclava, la biblioteca esclava no puede ejecutar el binlog pasado desde la biblioteca principal en este momento, lo que provocará el retraso maestro-esclavo.

Afortunadamente, el motor de almacenamiento InnoDB admite transacciones. Mysqldump tiene un parámetro de transacción única, que puede crear una instantánea coherente en la transacción y luego realizar una copia de seguridad de todas las tablas. Con este parámetro, los datos se pueden modificar durante la copia de seguridad, por lo que se recomienda utilizar el motor de almacenamiento InnoDB en el desarrollo normal.

Bloqueo a nivel de mesa

Hay dos tipos de bloqueos a nivel de tabla, uno es bloqueos de tabla y el otro es bloqueos de metadatos.

Bloqueo de mesa

Los bloqueos de tabla se dividen en bloqueos de lectura de tabla y bloqueos de escritura de tabla. Los comandos en MySQL son:

# 表读锁
lock tables test read;

# 表写锁
lock tables test write;

A continuación, a través de experimentos, observe cuál es la diferencia entre el bloqueo de lectura de la tabla y el bloqueo de escritura de la tabla.

Bloqueo de lectura de mesa

Sesión 1 sesión2
lectura de prueba de tablas de bloqueo;
seleccione * del límite de prueba1;
(devuelve los resultados normalmente)
seleccione * del límite de prueba 1;
(devuelva los resultados normalmente)
insertar en la prueba (a, b, c) valores (6,6,6);
(informe de error)
insertar en test (a, b, c) valores (8,8,8); # sql1
(bloqueo)
desbloquear tablas;
insertar en los valores de prueba (a, b, c) (8,8,8); # sql1
(después de desbloquear session1, sql1 se escribe con éxito inmediatamente)

Se agrega un bloqueo de lectura de tabla a la sesión session1. En este momento, tanto la sesión1 como la sesión2 pueden leer datos normalmente, pero la sesión1 informará un error al escribir datos y la sesión2 se bloqueará. Después de desbloquear la sesión1, los datos de escritura de la sesión2 pueden ser ejecutado con éxito.

A partir de este experimento, se puede concluir que después de agregar el bloqueo de lectura de la tabla a la tabla, este hilo y otros hilos pueden leer datos, este hilo informará un error al escribir datos y otros hilos se bloquearán al escribir datos.

Bloqueo de escritura de tabla

Sesión 1 sesión2
bloquear tablas de prueba de escritura;
seleccione * de la prueba limi1;
(devuelve los resultados normalmente)
seleccione * del límite de prueba 1; # sql1
(bloqueo)
desbloquear tablas;
seleccione * del límite de prueba; # sql1
(después de desbloquear la sesión1, sql1 devuelve el resultado inmediatamente)
bloquear tablas de prueba de escritura;
insertar en la prueba (a, b, c) valores (6,6,6);
(insertar correctamente)
insertar en la prueba (a, b, c) valores (8,8,8); # sql 2
(bloqueo)
desbloquear tablas;
insertar en los valores de prueba (a, b, c) (8,8,8); # sql2
(Después de desbloquear la sesión1, sql2 se ejecuta inmediatamente)

A partir del experimento anterior, se puede concluir que después de agregar el bloqueo de escritura de la tabla a la tabla, este hilo puede realizar operaciones de lectura y escritura, y se bloquearán las operaciones de lectura y escritura de otros hilos.

Bloqueo de metadatos (bloqueo de metadatos, denominado: bloqueo MDL)

En MySQL, el DDL de la base de datos no pertenece a la categoría de transacción. Si selecciona una fila de datos en session1, en este momento session2 agrega una nueva columna xxx a esta tabla. En este momento, puede haber errores como corruptos características de la transacción y secuencia binlog desordenada (Se han publicado errores similares en el sitio web oficial de MySQL, puede averiguarlo si está interesado).

为了解决以上的问题,从MySQL5.5.3引入了元数据锁,MDL锁不需要显式使用,MySQL会默认加上,它的作用就是保证数据库读写正确性。以下全部用MDL表示元数据锁。

当你对一张表进行增删查改的时候会默认加上MDL读锁;当你对一张表进行表结构更改的时候会默认加上MDL写锁。

session1 session2 session3 session4
begin;
select * from test lmi1;
(正常返回结果)
select * from test limit 1;
(正常返回结果)
alter table test add d int;
(阻塞)
select * from test limit 1;
(阻塞)

一开始session1会话查询test的时候,获取到了MDL读锁,可以正常查询到数据。然后session2会话查询数据也会获取MDL读锁,不冲突,也可以正常查询到数据返回。

但是到了session3会话的时候,需要获取MDL写锁,这个时候因为session1的MDL读锁没有释放,所以会阻塞。后面session4也需要MDL读锁,但是因为session3被阻塞了,所以session4也会被阻塞。

假如这是一张线上业务表,这种场景将会使后面的任何操作都失效,表现出来就是这张表变得无法写和读。如果客户端配置了MySQL重试机制的话,会在超时的时候重新建立一个session会话重新请求,然后MySQL就会因为线程不停新增而崩溃。

从上面的例子可以知道MDL锁是在语句执行的时候默认加上的,但是语句执行完是不会释放的,只有等整个事务提交了才会释放MDL锁。

所以对于我们开发者来说,在工作中应该尽量避免满查询、尽量保证事务及时提交、避免大事务等,对于DBA来说,也应该尽量避免在业务高峰期执行DDL操作。

总结

  • 全局锁会让所有的表变成只读状态,所有更新操作都会被阻塞
  • 表读锁是本线程和其他线程都可以读,本线程写会报错,其他线程写会阻塞
  • 表写锁是本线程可以读写,其他线程读写都会阻塞
  • 引入MDL锁解决事务和DDL同时执行引发的bug

参考资料

  • 《深入浅出MySQL》第二版:20.3.8 什么时候使用表锁
  • 《MySQL实战45讲》林晓斌

Supongo que te gusta

Origin blog.csdn.net/weixin_37686415/article/details/114184773
Recomendado
Clasificación