Una transacción es un conjunto de operaciones SQL que se ejecutan como una unidad indivisible: o se completan todas, o no se ejecuta ninguna. Son la herramienta que garantiza la consistencia de los datos cuando varias tablas se modifican juntas o cuando múltiples usuarios operan sobre los mismos datos al mismo tiempo.
En este capítulo cubrimos las propiedades ACID, la sintaxis de transacciones en MySQL, los niveles de aislamiento y los bloqueos. El motor InnoDB (predeterminado en MySQL desde la versión 5.5) es transaccional; MyISAM no soporta transacciones.
Las cuatro propiedades ACID
|
Propiedad | Significado |
Atomicidad | Todo o nada: si una instrucción falla, se deshace todo el bloque. |
Consistencia | La BD pasa de un estado válido a otro estado válido; nunca queda a medias. |
Isolamiento | Las transacciones concurrentes no se interfieren entre sí. |
Durabilidad | Un COMMIT confirmado sobrevive a fallos del sistema (se guarda en disco). |
Sintaxis básica
-- Por defecto, MySQL opera en autocommit: cada instrucción es su propia transacción
-- Para agrupar varias instrucciones en una transacción:
START TRANSACTION; -- o equivalentemente: BEGIN;
-- Crear el pedido
INSERT INTO pedidos (id_cliente, estado) VALUES (1, 'pendiente');
SET @pedido_id = LAST_INSERT_ID();
-- Añadir líneas
INSERT INTO lineas_pedido (id_pedido, id_producto, cantidad, precio_ud)
VALUES (@pedido_id, 1, 2, 59.99);
INSERT INTO lineas_pedido (id_pedido, id_producto, cantidad, precio_ud)
VALUES (@pedido_id, 2, 5, 8.99);
-- Actualizar el total del pedido
UPDATE pedidos
SET total = (
SELECT SUM(cantidad * precio_ud) FROM lineas_pedido WHERE id_pedido = @pedido_id
)
WHERE id = @pedido_id;
COMMIT; -- confirmar: todos los cambios se guardan permanentemente
-- Si algo falla antes del COMMIT:
ROLLBACK; -- deshacer todos los cambios desde el START TRANSACTION
SAVEPOINT: puntos de control dentro de la transacción
START TRANSACTION;
INSERT INTO pedidos (id_cliente, estado) VALUES (2, 'pendiente');
SAVEPOINT sp1;
-- Si este INSERT falla, podemos volver al savepoint sin deshacer todo
INSERT INTO lineas_pedido (id_pedido, id_producto, cantidad, precio_ud)
VALUES (LAST_INSERT_ID(), 99, 1, 0.00); -- producto 99 podría no existir
ROLLBACK TO SAVEPOINT sp1; -- vuelve al estado después del primer INSERT
-- El pedido sigue existiendo; solo se deshizo el INSERT de línea de pedido
COMMIT;
Niveles de aislamiento
El nivel de aislamiento controla qué puede leer una transacción de los cambios no confirmados de otras transacciones concurrentes:
Nivel | Dirty read | Non-repeatable read | Phantom read |
READ UNCOMMITTED | Posible | Posible | Posible |
READ COMMITTED | No | Posible | Posible |
REPEATABLE READ (defecto MySQL) | No | No | No* (InnoDB) |
SERIALIZABLE | No | No | No |
-- Ver el nivel actual SELECT @@transaction_isolation; -- Cambiar para la sesión actual SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
Deadlocks: bloqueos mutuos
Un deadlock ocurre cuando dos transacciones se bloquean mutuamente esperando que la otra libere un recurso. MySQL detecta los deadlocks automáticamente y aborta una de las transacciones (la que haya hecho menos trabajo). La aplicación debe relanzar la operación cuando recibe el error 1213 (Deadlock found).
-- Para minimizar deadlocks: -- 1. Bloquear siempre las tablas en el mismo orden -- 2. Mantener las transacciones lo más cortas posible -- 3. Usar índices para que los bloqueos de fila sean más selectivos -- Ver el último deadlock detectado por InnoDB: SHOW ENGINE INNODB STATUS\G
