Table Locking

MySQL permite bloquear tablas completas con LOCK TABLES para controlar el acceso concurrente. Esto es diferente de los bloqueos de fila que InnoDB maneja automáticamente.

Sintaxis

-- Bloqueo de lectura (compartido)
LOCK TABLES nombre_tabla READ;
 
-- Bloqueo de escritura (exclusivo)
LOCK TABLES nombre_tabla WRITE;
 
-- Bloquear múltiples tablas
LOCK TABLES tabla1 READ, tabla2 WRITE;
 
-- Desbloquear todas las tablas
UNLOCK TABLES;

Bloqueo de lectura (READ)

Un bloqueo READ permite que todas las sesiones lean la tabla, pero nadie puede escribir.

-- Sesión 1: Bloquear para lectura
LOCK TABLES productos READ;
 
-- Leer funciona normalmente
SELECT COUNT(*) FROM productos;
 
-- No puedes escribir en la tabla bloqueada
-- INSERT INTO productos (nombre, precio, stock, categoria_id)
-- VALUES ('Test', 10, 1, 1);
-- Error 1099: Table 'productos' was locked with a READ lock
 
-- Tampoco puedes leer otras tablas (no bloqueadas)
-- SELECT * FROM categorias;
-- Error 1100: Table 'categorias' was not locked with LOCK TABLES
 
UNLOCK TABLES;

Otras sesiones pueden leer la tabla pero si intentan escribir, se bloquean hasta que se libere el bloqueo:

Sesión 1:

LOCK TABLES productos READ;
-- Realizar consultas...

Sesión 2:

-- Leer funciona
SELECT * FROM productos WHERE id = 1;
 
-- Escribir se bloquea (espera)
UPDATE productos SET stock = stock - 1 WHERE id = 1;
-- Esperando...

Sesión 1:

UNLOCK TABLES;
-- Ahora el UPDATE de la sesión 2 se ejecuta

Bloqueo de escritura (WRITE)

Un bloqueo WRITE da acceso exclusivo: solo la sesión que tiene el bloqueo puede leer y escribir.

-- Sesión 1: Bloquear para escritura
LOCK TABLES productos WRITE;
 
-- Leer y escribir funciona
SELECT COUNT(*) FROM productos;
 
INSERT INTO productos (nombre, precio, stock, categoria_id)
VALUES ('Lock test', 10.00, 1, 1);
 
DELETE FROM productos WHERE nombre = 'Lock test';
 
UNLOCK TABLES;

Otras sesiones no pueden ni leer ni escribir:

Sesión 1:

LOCK TABLES productos WRITE;

Sesión 2:

-- Ni siquiera la lectura funciona (se bloquea)
SELECT * FROM productos WHERE id = 1;
-- Esperando...

Comparación READ vs WRITE

CaracterísticaREADWRITE
Sesión actual: lectura
Sesión actual: escrituraNo
Otras sesiones: lecturaBloqueadas
Otras sesiones: escrituraBloqueadasBloqueadas

Bloquear múltiples tablas

Cuando necesitas acceder a varias tablas, debes bloquearlas todas en una sola sentencia:

LOCK TABLES productos READ, categorias READ;
 
-- Ahora puedes consultar ambas tablas
SELECT p.nombre, c.nombre AS categoria
FROM productos p
JOIN categorias c ON p.categoria_id = c.id;
 
UNLOCK TABLES;

Si usas alias en la consulta, debes declararlos en el bloqueo:

LOCK TABLES productos AS p READ, categorias AS c READ;
 
SELECT p.nombre, c.nombre
FROM productos AS p
JOIN categorias AS c ON p.categoria_id = c.id;
 
UNLOCK TABLES;

Table locking vs Row locking

CaracterísticaTable LockingRow Locking (InnoDB)
GranularidadTabla completaFilas individuales
ConcurrenciaBajaAlta
OverheadBajoMayor
DeadlocksImposiblesPosibles
MotorTodosSolo InnoDB
Uso con transaccionesNo recomendado

Bloqueos y transacciones

LOCK TABLES y las transacciones InnoDB no funcionan bien juntos. LOCK TABLES causa un COMMIT implícito:

START TRANSACTION;
 
INSERT INTO productos (nombre, precio, stock, categoria_id)
VALUES ('Trans Lock', 10.00, 1, 1);
 
-- LOCK TABLES causa COMMIT implícito del INSERT anterior
LOCK TABLES productos WRITE;
 
-- No puedes hacer ROLLBACK del INSERT
ROLLBACK; -- No revierte nada
 
UNLOCK TABLES;
DELETE FROM productos WHERE nombre = 'Trans Lock';

Para bloqueos a nivel de fila dentro de transacciones, usa SELECT ... FOR UPDATE:

START TRANSACTION;
 
-- Bloquea solo la fila con id = 1
SELECT * FROM productos WHERE id = 1 FOR UPDATE;
 
UPDATE productos SET stock = stock - 1 WHERE id = 1;
 
COMMIT;

Caso práctico: exportación consistente

Bloquear tablas para obtener una instantánea consistente durante una exportación:

-- Bloquear todas las tablas necesarias
LOCK TABLES
    productos READ,
    categorias READ,
    pedidos READ,
    detalle_pedidos READ;
 
-- Realizar la exportación
SELECT * FROM productos INTO OUTFILE '/tmp/productos.csv'
FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n';
 
SELECT * FROM pedidos INTO OUTFILE '/tmp/pedidos.csv'
FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n';
 
-- Liberar bloqueos
UNLOCK TABLES;

En la práctica, es mejor usar mysqldump --single-transaction para tablas InnoDB.

Caso práctico: mantenimiento de tabla MyISAM

Las tablas MyISAM no soportan transacciones, por lo que LOCK TABLES es la única forma de garantizar consistencia:

-- Crear tabla MyISAM de ejemplo
CREATE TABLE log_accesos (
    id INT AUTO_INCREMENT PRIMARY KEY,
    pagina VARCHAR(200),
    fecha DATETIME DEFAULT NOW()
) ENGINE = MyISAM;
 
INSERT INTO log_accesos (pagina) VALUES ('/inicio'), ('/productos'), ('/contacto');
-- Bloquear para mantenimiento
LOCK TABLES log_accesos WRITE;
 
-- Archivar y limpiar
CREATE TABLE log_accesos_backup LIKE log_accesos;
INSERT INTO log_accesos_backup SELECT * FROM log_accesos;
DELETE FROM log_accesos;
 
UNLOCK TABLES;
-- Limpieza
DROP TABLE IF EXISTS log_accesos;
DROP TABLE IF EXISTS log_accesos_backup;

FLUSH TABLES WITH READ LOCK

Bloquea todas las tablas de todas las bases de datos para lectura. Útil para backups a nivel de sistema de archivos:

-- Bloqueo global de solo lectura
FLUSH TABLES WITH READ LOCK;
 
-- Realizar backup del sistema de archivos
-- (copia de archivos de datos)
 
-- Liberar
UNLOCK TABLES;

Verificar bloqueos activos

-- Ver bloqueos actuales (MySQL 8.0+)
SELECT * FROM performance_schema.metadata_locks
WHERE OBJECT_TYPE = 'TABLE'
AND LOCK_STATUS = 'GRANTED'\G
-- Ver conexiones esperando bloqueos
SHOW PROCESSLIST;

Cuándo usar table locking

EscenarioRecomendación
Tablas InnoDB normalesUsar transacciones y bloqueos de fila
Tablas MyISAMUsar LOCK TABLES cuando se necesite consistencia
Backup consistenteUsar mysqldump --single-transaction (InnoDB)
Mantenimiento masivoConsiderar LOCK TABLES para evitar interferencia
Acceso concurrente altoPreferir bloqueos de fila (InnoDB)

Resumen

  • LOCK TABLES READ permite lectura compartida, bloquea escrituras
  • LOCK TABLES WRITE da acceso exclusivo total
  • UNLOCK TABLES libera todos los bloqueos
  • Preferir transacciones InnoDB y FOR UPDATE sobre LOCK TABLES
  • LOCK TABLES causa COMMIT implícito, no mezclar con transacciones

Con esto completamos la sección de transacciones. En la siguiente sección exploraremos las funciones de cadena en MySQL.

Escrito por Eduardo Lázaro