Transacciones SQL y ACID explicados: COMMIT, ROLLBACK y niveles de aislamiento

  1. Inicio
  2. Blog
  3. Bases de datos y SQL

Bases de datos y SQL

Transacciones SQL y ACID explicados: COMMIT, ROLLBACK y niveles de aislamiento

¿Qué sucede si el servidor falla a la mitad de la transferencia de dinero entre dos cuentas? Las transacciones son la respuesta. Vea cómo funcionan en un ejemplo real, qué significa ACID y cómo elegir un nivel de aislamiento.

9 min de lectura
Transacciones SQL y ACID explicadas: COMMIT, ROLLBACK y niveles de aislamiento

Puntos clave

  • Una transacción agrupa declaraciones para que todas tengan éxito juntas o todas fallen juntas.
  • COMMIT hace que los cambios sean permanentes y ROLLBACK los cancela.
  • ACID significa atomicidad, consistencia, aislamiento y durabilidad.
  • Un aislamiento más fuerte evita más anomalías, pero puede reducir la concurrencia, así que elija deliberadamente.

Mover 300 rupias de Asha a Ben requiere dos actualizaciones: restar de una cuenta y agregar a la otra. Si el sistema falla entre ellos, el dinero desaparece. Las transacciones SQL existen para hacer esto imposible, al tratar ambos pasos como una sola unidad.

En esta guía analizamos confirmaciones, reversiones y fallas en una tabla de cuentas pequeñas. Ejecutamos esos ejemplos en SQLite y los saldos que se muestran provienen de esa ejecución. Los comandos básicos son similares en todas las bases de datos, pero el comportamiento de bloqueo y aislamiento varía mucho, por lo que le indicamos dónde consultar la documentación de su propia base de datos.

¿Qué es una transacción?

Una transacción es una secuencia de una o más sentencias SQL que la base de datos trata como una única operación lógica. Aquí está nuestra tabla:

CREATE TABLE accounts (
  id INT PRIMARY KEY,
  owner TEXT,
  balance INT CHECK (balance >= 0)
);

INSERT INTO accounts VALUES (1, 'Asha', 1000), (2, 'Ben', 200);

La restricción CHECK es una regla según la cual un saldo nunca puede bajar de cero, que usaremos más adelante. Tenga en cuenta que la sintaxis para iniciar una transacción varía: SQLite y PostgreSQL aceptan BEGIN, MySQL también tiene START TRANSACTION y SQL Server usa BEGIN TRANSACTION.

Fuera de una transacción explícita, la mayoría de las bases de datos utilizan autocommit, donde cada declaración se guarda tan pronto como finaliza. Oracle es una excepción notable, porque una sesión allí permanece en una transacción hasta que usted se compromete.

COMPROMISO: hacerlo permanente

BEGIN;
UPDATE accounts SET balance = balance - 300 WHERE id = 1;
UPDATE accounts SET balance = balance + 300 WHERE id = 2;
COMMIT;

-- Asha | 700
-- Ben  | 500

Hasta que se ejecute COMMIT, los cambios son provisionales. Una vez que regresa exitosamente, la base de datos garantiza que están guardados. El total en ambas cuentas se mantuvo en 1200, que es el objetivo del ejercicio.

ROLLBACK: cancelar todo

ROLLBACK deshace todo desde que comenzó la transacción:

BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
SELECT owner, balance FROM accounts;
-- Asha | 600   (visible inside this transaction)
-- Ben  | 500

ROLLBACK;
SELECT owner, balance FROM accounts;
-- Asha | 700   (back to where it was)
-- Ben  | 500

También se produce una reversión automáticamente si la conexión se interrumpe o el servidor falla antes de realizar la confirmación, por lo que una transacción a medio terminar no sobrevive.

Cuando una declaración falla

He aquí un caso más realista. Asha intenta enviar 5000, que la cuenta no contiene. El crédito a Ben se ejecuta primero y luego el débito infringe la regla CHECK:

BEGIN;
UPDATE accounts SET balance = balance + 5000 WHERE id = 2;
UPDATE accounts SET balance = balance - 5000 WHERE id = 1;
-- error: CHECK constraint failed: balance >= 0

ROLLBACK;

-- Asha | 700
-- Ben  | 500

Sin la transacción, Ben se habría quedado con los 5000 y los libros estarían equivocados. Tenga en cuenta un detalle importante: una declaración fallida no siempre cancela la transacción por sí sola. En SQLite y MySQL, por ejemplo, las declaraciones anteriores pueden permanecer pendientes, por lo que su código debe emitir la reversión. En cambio, PostgreSQL marca toda la transacción como cancelada y rechaza más declaraciones hasta que usted retroceda.

En el código de la aplicación, siempre empareje la transacción con el manejo de errores. Aquí está la idea en Python, y nuestra guía para el manejo de excepciones de Python explica la sintaxis:

try:
    conn.execute("BEGIN")
    conn.execute(debit_sql, values)
    conn.execute(credit_sql, values)
    conn.execute("COMMIT")
except Exception:
    conn.execute("ROLLBACK")
    raise

Puntos de guardado

Un punto de guardado marca una posición dentro de una transacción, por lo que solo puedes deshacer parte del trabajo:

BEGIN;
UPDATE accounts SET balance = balance - 50 WHERE id = 1;
SAVEPOINT bonus;
UPDATE accounts SET balance = balance + 999 WHERE id = 2;
ROLLBACK TO bonus;
UPDATE accounts SET balance = balance + 50 WHERE id = 2;
COMMIT;

-- Asha | 650
-- Ben  | 550

El bono 999 se deshizo, mientras que el resto de la transacción continuó y se comprometió. Esto es útil para pasos opcionales en un proceso largo.

Propiedades ácidas explicadas

Propiedad Promesa en nuestro traslado
Atomicidad todo o nada Ambas actualizaciones suceden, o ninguna
consistencia Las reglas siguen vigentes después Ningún saldo puede romper la regla CHECK
Aislamiento El trabajo concurrente no interfiere Otros no ven un traspaso a medio terminar
Durabilidad Los datos comprometidos sobreviven al fracaso Un corte de energía después de COMMIT no pierde nada

La coherencia suele malinterpretarse. La base de datos aplica solo las reglas que usted declara, como restricciones y claves externas. No impedirá que su código transfiera la cantidad incorrecta. La durabilidad generalmente se logra con un registro de escritura anticipada, donde los cambios se registran de forma segura en el disco antes de que se confirme la confirmación.

¿Qué sucede con las transacciones concurrentes?

Abrimos dos conexiones a un archivo SQLite. El primero inició una transacción y actualizó una fila del 1 al 2, sin comprometerse aún. Esto es lo que vio la segunda conexión:

Connection B reads:   1                     -- no uncommitted data
Connection B writes:  database is locked    -- it has to wait
(A commits)
Connection B reads:   2                     -- now it sees the change

SQLite permite sólo un escritor a la vez, lo cual es más simple que las bases de datos del servidor, donde muchas transacciones pueden escribir diferentes filas simultáneamente. Pero el principio es el mismo: otras sesiones no deberían ver cambios no confirmados en la configuración normal, y las escrituras conflictivas tienen que esperar. En nuestra prueba, configuramos la segunda conexión para que se cerrara inmediatamente en lugar de esperar, por lo que el error apareció de inmediato.

Leer anomalías

Cuando las transacciones se superponen, pueden ocurrir ciertos problemas. El estándar SQL los define en términos de lo que puede observar una transacción:

anomalía que pasa
lectura sucia Lee un cambio que otra transacción no ha confirmado y es posible que luego se revierta
Lectura no repetible Lees una fila dos veces en una transacción y obtienes valores diferentes, porque alguien se comprometió en el medio.
lectura fantasma Ejecutas la misma consulta dos veces y obtienes filas adicionales o faltantes, porque alguien insertó o eliminó algunas

Otro problema es la actualización perdida. Dos sesiones leen un saldo de 100, cada una suma 10 y cada una escribe 110. Una actualización efectivamente desapareció, cuando la respuesta debería ser 120.

Niveles de aislamiento de SQL

Los niveles de aislamiento le permiten cambiar el rigor por la simultaneidad. Esto es lo que permite el estándar SQL en cada nivel:

Nivel lectura sucia Lectura no repetible lectura fantasma
Leer no comprometido Posible Posible Posible
Leer comprometido Prevenido Posible Posible
Lectura repetible Prevenido Prevenido Posible
Serializable Prevenido Prevenido Prevenido
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

La sintaxis y el comportamiento exactos dependen de su base de datos. Los valores predeterminados también difieren: PostgreSQL y SQL Server usan lectura confirmada, mientras que InnoDB de MySQL usa lectura repetible. Algunas bases de datos ofrecen garantías más estrictas que las que exige la norma, o tratan un nivel como otro. En PostgreSQL, por ejemplo, la lectura no confirmada se comporta como lectura confirmada. Lea siempre la documentación de su base de datos antes de confiar en un comportamiento específico.

Consejo: Para una actualización perdida, realice el cambio en una sola declaración, como en UPDATE accounts SET balance = balance + 10. Hacer el cálculo en su solicitud a partir de una lectura anterior es lo que abre la brecha.

Puntos muertos

Un punto muerto ocurre cuando dos transacciones tienen cada una un bloqueo que la otra necesita. La transacción A bloquea la fila 1 y quiere la fila 2, mientras que la transacción B bloquea la fila 2 y quiere la fila 1. Ninguna puede continuar.

Bases de datos como PostgreSQL, MySQL y SQL Server detectan esto y cancelan una transacción con un error. Ese es un comportamiento normal y su aplicación debe estar preparada para ello. Para reducir los puntos muertos, actualice siempre las filas en un orden coherente, como por ejemplo el ID de cuenta más bajo primero, mantenga las transacciones breves y vuelva a intentarlo cuando se elija a una como víctima.

Mejores prácticas

  • Mantenga las transacciones cortas. Las largas mantienen bloqueos y bloquean a otros usuarios.
  • Nunca espere a que haya un usuario o una API lenta dentro de una transacción. Haga ese trabajo antes de comenzar o después.
  • Manejar siempre los errores. Cada ruta debe terminar en una confirmación o una reversión.
  • Agregue restricciones. Le dan a la base de datos reglas reales que debe aplicar. Un buen diseño de mesa también ayuda.
  • Utilice buenos índices. Las búsquedas más rápidas significan que los bloqueos se mantienen durante menos tiempo.
  • Tenga cuidado con los reintentos. Vuelva a intentarlo en caso de interbloqueo, pero asegúrese de que una operación no se pueda aplicar dos veces.
  • Pruebe la simultaneidad a propósito. Muchos de estos errores aparecen solo bajo carga, por lo que las pruebas de integración básicas por sí solas pueden no revelarlos.

Conclusión

Las transacciones convierten varios pasos riesgosos en una operación segura, y ACID describe las garantías que la hacen confiable. Siempre que una acción empresarial afecte a más de una fila o tabla, pregúntese qué pasaría si se detuviera a mitad de camino. Si la respuesta le preocupa, envuélvala en una transacción. Para practicar, abra dos sesiones en una base de datos local e intente producir las anomalías usted mismo.

Preguntas frecuentes

¿Qué es una transacción en SQL?

Una transacción es un grupo de sentencias SQL tratadas como una unidad. O todos se guardan con COMMIT o se deshacen con ROLLBACK.

¿Qué son las propiedades ACID en una base de datos?

ACID significa atomicidad, consistencia, aislamiento y durabilidad. Juntos, garantizan que las transacciones sean todo o nada, mantienen intactas las reglas, no interfieren y sobreviven a las fallas.

¿Cuál es la diferencia entre COMMIT y ROLLBACK?

COMMIT guarda permanentemente todos los cambios realizados en la transacción. ROLLBACK los cancela y devuelve los datos a como estaban antes de que comenzara la transacción.

¿Qué es una lectura sucia?

Una lectura sucia es cuando una transacción lee cambios que otra aún no ha confirmado. Si esa otra transacción se revierte, los datos leídos nunca existieron realmente.

¿Qué nivel de aislamiento debo utilizar?

Comience con el valor predeterminado de su base de datos, que generalmente está bien. Utilice serializable para lógica crítica donde las anomalías sean inaceptables y esté preparado para volver a intentar transacciones fallidas.

sigue aprendiendo

Trabajar con Stack Quora

¿Necesitas ayuda con pruebas, WordPress o SEO?

Ayudamos a los equipos a enviar mejor software y hacer crecer sus sitios. Cuéntenos en qué está trabajando y le sugeriremos un siguiente paso claro.

Sigue leyendo

Más guías sobre testing, IA y desarrollo moderno

¿Te gustó este artículo? Elige un tema y salta directo a nuestros tutoriales más recientes.

Inteligencia artificial

Explicación de las alucinaciones de la IA: por qué los modelos de lenguaje inventan cosas y cómo reducirlas

Se explicaron las alucinaciones de la IA: qué son, por qué los modelos de lenguaje las producen, tipos comunes, cómo reducirlas con conexión a tierr…

Leer artículo →
DevOps

Explicación de las variables de entorno: archivos .env, secretos y configuración bien hechos

Variables de entorno explicadas con ejemplos de Python que ejecutamos: leerlas y configurarlas, por qué cada valor es una cadena, archivos .env, her…

Leer artículo →
Desarrollo de software

Explicación de la limitación de velocidad de API: algoritmos, encabezados y manejo de errores 429

La limitación de la tasa de API se explica con una simulación que ejecutamos: depósito de tokens, ventana fija, ventana deslizante, 429 respuestas, …

Leer artículo →
Automatización de pruebas

Burlarse en Python: unittest.mock, patch y los errores que hacen que las pruebas mientan

Burlarse en Python con unittest.mock, probado: Mock y return_value, side_effect, parches donde se usa un nombre, especificación y autospec, fechas d…

Leer artículo →
Pruebas de software

Explicación de la cobertura del código: cobertura de línea versus sucursal y lo que realmente significa 100%

Cobertura del código explicada con una ejecución real de cobertura.py: cobertura de línea, rama y condición, lectura de un informe, por qué una cobe…

Leer artículo →
Bases de datos y SQL

DELETE vs TRUNCATE vs DROP en SQL: diferencias, reversión y cuándo usar cada uno

DELETE vs TRUNCATE vs DROP explicado con SQL probado: qué elimina cada uno, reversión, activadores, restablecimiento de identidad, velocidad y en qu…

Leer artículo →
Bases de datos y SQL

Vistas SQL explicadas: cree, use y evite los errores, con ejemplos

Vistas SQL explicadas con consultas que ejecutamos: creación de una vista, resultados en vivo, ocultación de columnas, rendimiento, vistas actualiza…

Leer artículo →
Desarrollo de software

Interfaz TypeScript versus tipo: diferencias reales, probadas con el compilador

Interfaz TypeScript versus tipo, verificado con el compilador real: qué pueden hacer ambos, qué puede hacer solo uno, fusión de declaraciones, exten…

Leer artículo →
Desarrollo de software

Python *args y **kwargs explicados: funciones flexibles con ejemplos claros

Python *args y **kwargs explicados con el código que ejecutamos: empaquetado y desempaquetado, orden de parámetros, argumentos solo de palabras clav…

Leer artículo →
Desarrollo de software

Funciones Lambda de Python: sintaxis, usos reales y cuándo no utilizarlas

Funciones lambda de Python explicadas con el código que ejecutamos: sintaxis, clasificación con clave, mapa y filtro, cierres, trampa de enlace tard…

Leer artículo →
Inteligencia artificial

¿Qué son los agentes de IA? Cómo funcionan, con un bucle de agente simple que puedes ejecutar

¿Qué son los agentes de IA? Una guía sencilla sobre el bucle del agente, las herramientas, la memoria y las barreras de seguridad, con un pequeño bu…

Leer artículo →
DevOps

Monitoreo versus observabilidad: la verdadera diferencia, con registros, métricas y seguimientos

Explicación del monitoreo versus observabilidad: fallas conocidas y desconocidas, registros, métricas y seguimientos, un recorrido por incidentes de…

Leer artículo →
DevOps

Docker Volumes versus Bind Mounts: persistir los datos del contenedor de la manera correcta

Se explican los volúmenes de Docker frente a los montajes de enlace: por qué desaparecen los datos del contenedor, volúmenes con nombre, montajes de…

Leer artículo →
Automatización de pruebas

TDD vs BDD: diferencias clave, una función creada en ambos sentidos y cuándo usar cada una

TDD vs BDD explicado con una característica de descuento construida en ambos sentidos: refactor rojo-verde con el código que ejecutamos, escenarios …

Leer artículo →
Pruebas de software

Pruebas de Aceptación de Usuario (UAT): Proceso, Lista de Verificación y Ejemplos Reales

Explicación de las pruebas de aceptación del usuario: quién las ejecuta, en qué se diferencia del control de calidad, un proceso de siete pasos, una…

Leer artículo →
Bases de datos y SQL

Valores NULL de SQL explicados: IS NULL, COALESCE, NULLIF y las trampas que se deben evitar

Los valores NULL de SQL se explican con las consultas que ejecutamos: lógica de tres valores, IS NULL, COALESCE, NULLIF, cómo los agregados, las uni…

Leer artículo →
Bases de datos y SQL

Declaración SQL CASE WHEN: sintaxis, ejemplos reales y errores comunes

CASO SQL CUANDO se explicó con las consultas que ejecutamos: formularios simples y buscados, orden de condiciones, pivotes con SUMA, trucos COUNT, c…

Leer artículo →
Desarrollo de software

¿Qué es JWT? Tokens web JSON explicados con un ejemplo práctico

¿Qué es JWT? JSON Web Tokens explicó: las tres partes, un token creado y manipulado en el código que ejecutamos, JWT vs sesiones, almacenamiento, to…

Leer artículo →
Desarrollo de software

Explicación del bucle de eventos de JavaScript: pila de llamadas, microtareas y temporizadores con ejemplos

El bucle de eventos de JavaScript explicado con el código que ejecutamos: pila de llamadas, colas de tareas y microtareas, por qué setTimeout 0 no e…

Leer artículo →
Desarrollo de software

Explicación de los generadores y el rendimiento de Python: ahorre memoria con ejemplos reales

Los generadores de Python y el rendimiento se explican con el código que ejecutamos: cómo se pausan y reanudan, midieron el ahorro de memoria, canal…

Leer artículo →
Inteligencia artificial

¿Qué son las incrustaciones? Los vectores y la similitud del coseno se explican de forma sencilla

¿Qué son las incrustaciones? Una guía en lenguaje sencillo sobre vectores, similitud de cosenos y búsqueda semántica, con un pequeño ejemplo que eje…

Leer artículo →
DevOps

Implementación azul-verde versus implementación canaria: diferencias, compensaciones y cómo elegir

Explicación de la implementación azul-verde versus canaria: cómo funciona cada uno, costo, riesgo y reversión, agrupación estable de usuarios que pr…

Leer artículo →
Automatización de pruebas

Pruebas inestables: qué las causa y cómo solucionarlas para siempre

Pruebas inestables explicadas con demostraciones que realizamos: las causas principales, cómo confirmar y diagnosticar una, correcciones adecuadas p…

Leer artículo →
Pruebas de software

Pruebas de carga frente a pruebas de estrés: diferencias, métricas y ejemplos de k6

Explicación de las pruebas de carga frente a las pruebas de estrés: qué prueba cada una, pruebas de picos y de absorción, las métricas que importan,…

Leer artículo →
Pruebas de software

Caso de prueba frente a escenario de prueba: diferencias clave, ejemplos y cuándo utilizar cada uno

Caso de prueba versus escenario de prueba explicado: definiciones claras, una tabla en paralelo, una función de restablecimiento de contraseña tomad…

Leer artículo →
Bases de datos y SQL

Optimización de consultas SQL: lea planes de ejecución y corrija consultas lentas, con tiempos

Optimización de consultas SQL probada en un millón de filas: lea planes de ejecución, agregue los índices correctos, corrija filtros no sargables, u…

Leer artículo →
Bases de datos y SQL

SQL UNION vs UNION ALL: diferencias, ejemplos y rendimiento probado

SQL UNION vs UNION ALL explicado con consultas que ejecutamos: duplicados, las reglas que siguen ambos, un error de datos real, rendimiento medido, …

Leer artículo →
Desarrollo de software

Tutorial de JavaScript Fetch API: GET, POST, manejo de errores y tiempos de espera

Un tutorial de JavaScript Fetch API probado en un servidor local: GET y POST, por qué 404 no arroja, un asistente reutilizable, cadenas de consulta,…

Leer artículo →
Desarrollo de software

Manejo de archivos Python: lectura, escritura, CSV, JSON y pathlib con ejemplos

El manejo de archivos Python se explica con el código que ejecutamos: modos abiertos, lectura de archivos grandes, CSV, JSON, pathlib, archivos bina…

Leer artículo →
Desarrollo de software

Clases y objetos de Python: tutorial de programación orientada a objetos con ejemplos reales

Clases y objetos de Python explicados con el código que ejecutamos: __init__, métodos, atributos de clase versus instancia, herencia, propiedades, m…

Leer artículo →
Inteligencia artificial

¿Qué es un modelo de lenguaje grande? Cómo funcionan los LLM, explicados sin exageraciones

¿Qué es un modelo de lenguaje grande? Los LLM explicados en lenguaje sencillo: tokens, incrustaciones, atención, entrenamiento, ventanas de temperat…

Leer artículo →
DevOps

Comandos de Linux para principiantes: la guía esencial de terminal con ejemplos

Comandos de Linux para principiantes, agrupados por tarea: navegación, archivos, visualización de registros, grep, canalizaciones, permisos, proceso…

Leer artículo →
Automatización de pruebas

Selenium vs Playwright: diferencias clave, comparación de códigos y cuál elegir

Comparación de Selenium vs Playwright: arquitectura, espera, soporte de navegador e idioma, depuración, la misma prueba en ambas herramientas, conse…

Leer artículo →
Pruebas de software

Partición de equivalencia: técnica de diseño de pruebas explicada con ejemplos resueltos

Explicación de la partición de equivalencia: cómo encontrar particiones válidas e inválidas, ejemplos prácticos para edades, contraseñas y cupones, …

Leer artículo →
Pruebas de software

Severidad vs Prioridad en Pruebas de Software: Diferencias, Matriz y Ejemplos Reales

Explicación de gravedad versus prioridad en las pruebas de software: definiciones claras, una matriz de cuatro vías con ejemplos, escalas de niveles…

Leer artículo →
Bases de datos y SQL

Tutorial de subconsultas SQL: tipos, subconsultas correlacionadas y EXISTENCIAS con ejemplos

Un tutorial de subconsultas SQL con consultas que ejecutamos: subconsultas escalares, IN, FROM y SELECT, subconsultas correlacionadas, EXISTS, la tr…

Leer artículo →
Bases de datos y SQL

Transacciones SQL y ACID explicados: COMMIT, ROLLBACK y niveles de aislamiento

Transacciones SQL y ACID explicadas con una transferencia bancaria que ejecutamos: COMMIT, ROLLBACK, puntos de guardado, bloqueos, niveles de aislam…

Leer artículo →
Desarrollo de software

Git merge vs rebase: diferencias, ejemplos y cuándo usar cada uno

Git merge vs rebase explicado con salida real del log, una tabla comparativa, la regla de oro del rebase y un flujo de trabajo seguro para equipos.

Leer artículo →
Desarrollo de software

List comprehension en Python: sintaxis, ejemplos y cuándo evitarla

List comprehension en Python explicada: sintaxis, filtros, if-else, bucles anidados, tiempos medidos y cuándo es mejor un bucle for normal.

Leer artículo →