6.4 Transacciones¶
Una transacción agrupa varias sentencias para que se ejecuten como una sola operación: o se hacen todas, o no se hace ninguna.
Por qué hacen falta¶
Imagina cambiar a un alumno de grupo y actualizar a la vez un contador. Si el programa se cae entre las dos sentencias, la base de datos queda a medias: incoherente. Con una transacción, o se aplican las dos o ninguna.
BEGIN, COMMIT y ROLLBACK¶
| Sentencia | Qué hace |
|---|---|
BEGIN (o START TRANSACTION) |
Empieza la transacción |
COMMIT |
Confirma: los cambios se guardan definitivamente |
ROLLBACK |
Deshace todo lo hecho desde el BEGIN |
Se ve mejor con una prueba arriesgada: dentro de una transacción se borran las notas de la segunda convocatoria, se comprueba, y se deshace:
BEGIN;
DELETE FROM notas WHERE convocatoria = 2;
SELECT COUNT(*) AS notas_dentro_de_la_transaccion FROM notas;
Resultado:
| notas_dentro_de_la_transaccion |
|---|
| 46 |
1 fila
Dentro de la transacción faltan notas. Pero si ahora se hace ROLLBACK:
BEGIN;
DELETE FROM notas WHERE convocatoria = 2;
ROLLBACK;
SELECT COUNT(*) AS notas_despues_del_rollback FROM notas;
Resultado:
| notas_despues_del_rollback |
|---|
| 57 |
1 fila
Las 57 notas siguen ahí: el ROLLBACK lo ha deshecho todo. Con COMMIT en lugar de ROLLBACK, los borrados habrían quedado confirmados.
Una red de seguridad
Antes de un UPDATE o DELETE importante: BEGIN;, ejecutarlo, comprobar el resultado, y solo entonces COMMIT; (o ROLLBACK; si algo no cuadra). Así es difícil romper algo sin querer.
Autocommit¶
Por defecto, la mayoría de los motores funcionan en autocommit: cada sentencia es una transacción en sí misma, que se confirma sola. Solo agrupas varias sentencias cuando escribes BEGIN (o desactivas el autocommit desde un programa, como se vio en la unidad de bases de datos de los lenguajes).
SAVEPOINT: deshacer solo una parte¶
Un punto de guardado permite deshacer solo lo posterior sin cancelar toda la transacción:
BEGIN;
UPDATE notas SET nota = 0 WHERE id_alumno = 1;
SAVEPOINT antes_del_borrado;
DELETE FROM notas WHERE id_alumno = 2;
ROLLBACK TO antes_del_borrado;
COMMIT;
SELECT (SELECT COUNT(*) FROM notas WHERE id_alumno = 2) AS notas_del_alumno_2,
(SELECT MAX(nota) FROM notas WHERE id_alumno = 1) AS maxima_del_alumno_1;
Resultado:
| notas_del_alumno_2 | maxima_del_alumno_1 |
|---|---|
| 4 | 0.0 |
1 fila
Se mantuvo el UPDATE (las notas del alumno 1 valen 0), pero se deshizo el borrado (el alumno 2 conserva sus 4 notas).
Las propiedades ACID¶
Un motor serio garantiza que las transacciones cumplen cuatro propiedades:
| Propiedad | Significa |
|---|---|
| Atomicidad | Todo o nada |
| Consistencia | Los datos siguen cumpliendo las reglas (claves, CHECK...) antes y después |
| Islamiento | Las transacciones simultáneas no se estorban: cada una ve los datos como si estuviera sola |
| Durabilidad | Lo confirmado no se pierde, aunque el equipo se apague |
Aislamiento y concurrencia¶
Cuando varias personas usan la base de datos a la vez, el motor aísla sus transacciones. Cuánto las aísla se configura con los niveles de aislamiento: más aislamiento significa más seguridad pero menos velocidad.
| Problema que puede ocurrir | Qué es |
|---|---|
| Lectura sucia | Ver datos de otra transacción que todavía no se ha confirmado (y quizá se deshaga) |
| Lectura no repetible | Leer dos veces la misma fila y obtener valores distintos, porque otra transacción la cambió entre medias |
| Lectura fantasma | Repetir una consulta y que aparezcan filas nuevas que otra transacción insertó |
| Nivel | Evita |
|---|---|
READ UNCOMMITTED |
Nada (permite lecturas sucias) |
READ COMMITTED |
Lecturas sucias |
REPEATABLE READ |
Lecturas sucias y no repetibles |
SERIALIZABLE |
Todos los problemas |
El nivel por defecto depende del motor: READ COMMITTED en PostgreSQL y Oracle, REPEATABLE READ en MySQL (InnoDB), y SQLite trabaja de forma serializable (una sola escritura a la vez).
Para proteger los datos mientras se modifican, los motores usan bloqueos: una transacción puede tener que esperar a otra. Si dos transacciones se esperan mutuamente, hay un interbloqueo (deadlock): el motor detecta la situación y cancela una de las dos, y el programa debe estar preparado para reintentarla.
Errores frecuentes¶
| Error | Cómo evitarlo |
|---|---|
| Hacer varios cambios relacionados sin transacción | Agruparlos con BEGIN ... COMMIT |
Olvidar el COMMIT y dejar la transacción abierta |
Siempre COMMIT o ROLLBACK: una transacción abierta puede bloquear a los demás |
| Transacciones largas | Cuanto más corta, menos bloquea |
No gestionar el error y no hacer ROLLBACK |
En un programa, try/catch con rollback |
| Asumir que un motor aísla como otro | Consulta el nivel por defecto del tuyo |
Para practicar¶
El ejercicio S6.7 de S6 · Ejercicios practica las transacciones.