2 puntos por GN⁺ 2026-02-24 | 1 comentarios | Compartir por WhatsApp
  • Una transacción es una estructura para ejecutar varias operaciones en una base de datos como una sola unidad atómica, e incluye lectura, escritura, actualización y eliminación
  • MySQL y Postgres controlan las transacciones con begin; y commit;, y cancelan los cambios con rollback; cuando hay fallas o errores
  • Ambas bases de datos garantizan lectura consistente (consistent read), pero Postgres usa almacenamiento de filas multiversión (MVCC) y MySQL usa undo log
  • El nivel de aislamiento (isolation level) controla la interferencia de datos entre transacciones y se divide en cuatro niveles, desde Serializable hasta Read Uncommitted
  • Postgres y MySQL manejan los conflictos de escritura concurrente de maneras distintas: Postgres usa validación optimista y MySQL bloqueo a nivel de fila (row-level locking)

Conceptos básicos de las transacciones

  • Una transacción es una estructura que agrupa varias operaciones SQL en una base de datos en una sola unidad de ejecución atómica
    • Empieza con begin; y termina con commit;, y permite ejecutar varias consultas entre ambos
    • En el momento de commit;, todos los cambios se aplican de una sola vez
  • Ante fallas inesperadas (corte de energía, error de disco, etc.) o una cancelación intencional, los cambios se revierten con rollback;
    • Postgres admite la recuperación mediante WAL (Write-Ahead Log)
  • Los datos modificados durante una transacción quedan aislados y no son visibles desde otras sesiones
    • Si se ejecuta rollback;, todos los cambios se cancelan y la base de datos vuelve a su estado original

Lectura consistente (Consistent Reads)

  • Una transacción debe mantener una vista consistente de los datos que no se vea afectada por cambios externos mientras se ejecuta
  • MySQL y Postgres lo soportan desde el modo REPEATABLE READ en adelante, pero lo implementan de forma distinta
    • Postgres: administra versiones de cada fila mediante almacenamiento de filas multiversión (MVCC)
    • MySQL: reconstruye versiones anteriores usando undo log

Almacenamiento de filas multiversión en Postgres

  • Cada vez que se actualiza una fila, se crea una nueva versión; la versión anterior registra el ID de transacción en xmax y la nueva versión en xmin
  • Antes de que la transacción haga commit, otras sesiones no pueden ver los cambios
  • Después del commit, la nueva versión se refleja en toda la base de datos
  • Si se ejecuta rollback;, los cambios se descartan y se conserva el dato original
  • Las versiones antiguas de las filas se limpian con el comando VACUUM FULL para recuperar espacio de almacenamiento

Undo Log de MySQL

  • MySQL sobrescribe directamente la fila, pero registra el valor anterior en el undo log para poder restaurarlo si es necesario
  • Cada fila tiene como metadatos xid (ID de la transacción que la modificó más recientemente) y ptr (puntero al undo log)
  • Cuando se ejecutan varias transacciones al mismo tiempo, cada una consulta selectivamente la versión que necesita a través del undo log
  • Puede haber varios registros de undo log para una misma fila, y la versión adecuada se elige con base en el ID de transacción

Niveles de aislamiento (Isolation Levels)

  • Es una configuración que controla la interferencia de datos entre transacciones y se flexibiliza en este orden: Serializable → Repeatable Read → Read Committed → Read Uncommitted
  • Serializable: todas las transacciones se comportan como si se ejecutaran secuencialmente
  • Repeatable Read: volver a ejecutar la misma consulta produce el mismo resultado, pero puede haber phantom read
  • Read Committed: se pueden leer cambios de otras transacciones que ya hicieron commit
  • Read Uncommitted: permite dirty read; es el nivel de protección más bajo, pero ofrece mayor rendimiento

Escrituras concurrentes (Concurrent Writes)

  • La forma de manejar dos transacciones que modifican la misma fila al mismo tiempo varía según la base de datos

MySQL: bloqueo a nivel de fila (Row-level Locking)

  • El bloqueo compartido (S lock) permite que varias transacciones lean al mismo tiempo
  • El bloqueo exclusivo (X lock) permite que solo una transacción modifique la fila
  • En modo SERIALIZABLE, cada actualización debe adquirir un X lock y, si hay conflicto, puede producirse un deadlock
  • MySQL detecta el deadlock y finaliza una de las transacciones

Postgres: Serializable Snapshot Isolation

  • Postgres usa predicate lock para rastrear accesos a nivel de conjunto de filas
    • Ejemplo: un bloqueo sobre la condición WHERE id BETWEEN 10 AND 20
  • No bloquea el acceso real, sino que detecta conflictos y finaliza la transacción cuando hay una violación
  • Usa resolución optimista de conflictos (optimistic conflict resolution) para evitar deadlocks
  • Igual que en MySQL, cuando hay conflicto una de las transacciones se finaliza y la aplicación debe implementar lógica de reintento

Conclusión

  • Las transacciones son un componente central de la base de datos y garantizan atomicidad, consistencia, aislamiento y durabilidad (ACID)
  • Postgres y MySQL alcanzan el mismo objetivo con estructuras internas diferentes
  • Comprender los cuatro niveles de aislamiento y el funcionamiento de las transacciones permite operar bases de datos de forma más estable

1 comentarios

 
GN⁺ 2026-02-24
Comentarios en Hacker News
  • Este artículo se sintió algo insuficiente.
    En vez de explicar los niveles de aislamiento centrados en los fenómenos (phenomena) definidos en el estándar SQL, parece más intuitivo partir del concepto de serializabilidad (serializability).
    La serializabilidad puede verse como una generalización de la seguridad de hilos, y perderla introduce bugs donde el resultado cambia según el orden de ejecución.
    Los distintos niveles de aislamiento de una base de datos no son más que formas de relajar esa garantía, y el usuario debe asegurarla por otros medios.
    Los fenómenos son solo una herramienta para visualizar situaciones no serializables; no están conectados directamente con la serializabilidad.
    Por ejemplo, incluso un clúster de Kubernetes puede comportarse de forma serializable si usa controladores bien diseñados.

    • Soy el autor. Gracias por el buen feedback.
      Cubrir de una sola vez transacciones, niveles de aislamiento y MVCC, incluyendo comparaciones entre varias BD, es una tarea enorme.
      Intenté equilibrar profundidad técnica, accesibilidad y longitud del artículo.
    • Comparte el enlace a Jepsen: análisis de MariaDB Galera Cluster.
      Opina que sería bueno incluir más notación y citas.
    • La mayoría de los RDBMS ofrecen aislamiento serializable cuando hace falta.
      Pero usarlo sin necesidad aumenta el costo de coordinación entre transacciones, reduciendo concurrencia y throughput.
    • Entonces responde que propongan una explicación mejor.
  • También se puede pensar en las transacciones como snapshots de un filesystem copy-on-write (btrfs, zfs), pero considera que la analogía con ramas de Git es más intuitiva.
    BEGIN es crear una rama, UPDATE es hacer commits, ROLLBACK es borrar la rama y COMMIT es como git merge.
    Si hay conflictos, la BD intenta hacer merge a nivel de fila y, si falla, según la configuración hace rollback o fuerza el merge.
    READ UNCOMMITTED prioriza merges rápidos y SERIALIZABLE prioriza la exactitud.
    Este tipo de analogía puede ayudar a que alguien tenga ese momento de “¡ah, ya entendí!” sobre el concepto de transacción.

    • (Comentario corto) Es una reacción que insinúa la concurrencia.
  • Lo que sorprende a mucha gente es que Postgres y MySQL no están por defecto en modo serializable, sino en read-committed.
    La diferencia de rendimiento no es “pequeña”, en la práctica es mucho mayor.
    Si usas read-committed, tienes que prestar atención al manejo de locks, y también hacen falta restricciones UNIQUE para evitar condiciones de carrera.
    Aun así, prefieren este enfoque antes que asumir la pérdida de rendimiento y los reintentos del modo serializable.
    Referencia: documentación oficial de PostgreSQL

    • En MySQL y MariaDB (InnoDB) modernos, el valor por defecto es repeatable-read.
      Ver documentación de MySQL y documentación de MariaDB.
      MyISAM ya casi no se usa.
    • El problema de SERIALIZABLE no es solo el rendimiento, sino que las transacciones pueden fallar por conflictos, deadlocks o timeouts.
      La aplicación debe detectar eso y tener una estrategia de reintento.
    • Oracle y SQL Server también usan read committed por defecto.
      El modo serializable se ve elegante en los libros de texto, pero en la práctica casi no se usa.
  • Hoy en día muchas herramientas de bases de datos priorizan compartir actualizaciones en tiempo real por encima de ACID.
    Por ejemplo, en Airtable, cuando se modifica un campo eso se refleja de inmediato en la pantalla de otros compañeros, pero como no hay transacciones existe riesgo de inconsistencia de datos.
    Sobre esto, ver la entrada del blog de VisualDB.

    • La respuesta es que parece promoción de producto disfrazada de crítica a la competencia.
  • Da muchísimo gusto leer el blog de PlanetScale.
    Tiene curiosidad por saber qué herramienta usaron para las visualizaciones.

    • Soy el autor. ¡Gracias!
      Las visualizaciones se hicieron con js + gsap(https://gsap.com).
  • Si te interesa este tema, recomiendan muchísimo 『Designing Data-Intensive Applications』.
    Cubre no solo distintos niveles de aislamiento, sino también la ambigüedad en la definición de ACID.
    Escucharon que la segunda edición sale pronto.

  • Las transacciones en un sistema MVCC como Postgres se parecen a los snapshots de un filesystem copy-on-write.
    En el momento de BEGIN se crea un snapshot de los datos, y UPDATE solo se refleja en una copia privada.
    Si ocurre ROLLBACK, esa copia se descarta, y con COMMIT el nuevo snapshot pasa a ser la versión oficial.
    Esta analogía puede ser el punto que le aclare a alguien cómo funcionan las transacciones.
    P.D. También se puede usar la analogía de las ramas de Git.

    • No es del todo exacto. La BD usa branching y locking a la vez.
      En casos como SELECT seguido de UPDATE, un hilo puede quedar bloqueado.
      Hoy piensa probar en MySQL si eso se puede convertir en una sola query.
  • Antes era común preguntar sobre transacciones en entrevistas para backend.
    Todo el mundo las había usado, pero el nivel de comprensión variaba según la experiencia.
    Aunque no memoricen todos los niveles de aislamiento, con solo saber que se comportan distinto ya se puede ver la curiosidad y el entendimiento de sistemas de alguien.

    • Incluso niveles de aislamiento con el mismo nombre pueden comportarse distinto según la BD, así que hay que revisar el comportamiento detallado en cada caso.
  • La explicación de “phantom read” puede llevar a confusión.
    En repeatable read, los valores de las filas existentes no cambian, pero sí pueden agregarse filas nuevas.
    Como las filas existentes no cambian ni se eliminan, convendría dejar eso más claro.

  • La frase “no tiene relación con xmin/xmax” se siente incompleta.
    También resulta extraño que en la visualización, al hacer commit, se señale el encabezado de la tabla.
    En realidad, ¿xmax/xmin no son el mecanismo clave para determinar si algo fue committeado?
    Si se consideran subtransacciones, se vuelve aún más complejo.
    Aun así, en general la visualización y la explicación fueron agradables de leer.

    • A mí también me pareció una pena que faltara el concepto de xmax/xmin.
      Es clave para entender los niveles de aislamiento, y daba la impresión de que faltaba toda una sección.