Saltar al contenido

046 — Registro anticipado y recuperación: WAL y ARIES

🗂️ parte 🎚️ nivel ⏱️ duración 📗 clase

Programa · Parte 08 · ← Anterior · Siguiente →

Parte 08 — Transacciones, concurrencia y recuperación · Avanzado · 4 horas estimadas · motores postgresql, sqlite · laboratorio labs/03-transactions · 3 fuentes.

Conceptos centrales: WAL · punto de control · rehacer · deshacer · LSN

En este caso se comparan 7 motores: 6 lo resuelven (0 con el resultado comprobado por máquina) y 1 no, con el motivo escrito.

De qué trata esta clase

Cómo se vuelve de una caída. El registro anticipado escribe la intención antes que el dato, el punto de control acorta la recuperación y ARIES ordena las fases de rehacer y deshacer de modo que repetirlas sea inofensivo —lo que permite recuperarse de una caída ocurrida durante la recuperación.

flowchart LR
    C["🗄️ Clase 046"]
    C --> K1["WAL"]
    C --> K2["punto de control"]
    C --> K3["rehacer"]
    C --> K4["deshacer"]
    C --> K5["LSN"]
    classDef raiz fill:#0b3d2e,stroke:#3fb950,color:#fff
    class C raiz

Antes de empezar

Esta clase supone que ya trabajaste lo siguiente. Si algo de la última columna no te suena, vuelve a esa clase antes de seguir: aquí se usa sin volver a explicarlo.

# Clase previa Lo que se da por sabido
043 ACID: qué garantiza cada letra y quién la implementa atomicidad · consistencia · aislamiento · durabilidad · unidad de recuperación

Vocabulario de la clase

Los términos que siguen se usan más adelante con este significado exacto. La definición completa, con sus términos relacionados, está en el glosario del programa.

Término Qué significa Procedencia
WAL Registro anticipado: antes de tocar la página de datos se escribe en un registro secuencial qué se va a cambiar, y ese registro se fuerza al disco antes de confirmar. Es lo que hace posible la durabilidad sin escribir cada página en cada COMMIT. se introduce aquí
punto de control Marca periódica que fija hasta dónde están ya volcadas a disco las páginas modificadas. Acorta la recuperación, porque tras una caída solo hay que releer el registro desde el último punto de control y no desde el principio de los tiempos. se introduce aquí
rehacer Fase de la recuperación que reaplica desde el registro todo lo confirmado que aún no había llegado a las páginas de datos. ARIES la ejecuta antes de deshacer y de forma que repetirla sea inofensiva, lo que permite recuperarse de una caída ocurrida durante la recuperación. se introduce aquí
deshacer Fase que revierte las transacciones que estaban a medias en el momento de la caída, usando la información de deshacer del registro. Es la implementación concreta de la atomicidad. se introduce aquí
LSN Número de secuencia del registro: identifica cada entrada del WAL en orden y se estampa en la página que modifica. Permite saber, página por página, si un cambio ya está aplicado —y por eso rehacer se puede repetir sin efectos secundarios. se introduce aquí

Propósito

Entender cómo un motor sobrevive a una caída: el registro anticipado y el algoritmo de recuperación. Es el mecanismo que hace real la D de ACID, y explica muchas decisiones de configuración.

Resultados de aprendizaje

Al terminar podrás:

  1. Enunciar la regla del registro anticipado y por qué es necesaria.
  2. Describir las tres fases de ARIES y qué garantiza cada una.
  3. Explicar qué es un punto de control y qué acorta.
  4. Relacionar fsync con la durabilidad real ante cada tipo de fallo.
  5. Leer el modo WAL de SQLite como ejemplo completo y pequeño.

Fundamentos

La regla

Antes de escribir una página modificada al disco, debe estar en disco el registro que describe esa modificación.

Sin esa regla no hay recuperación posible: si la página llegara antes que el registro, tras una caída habría un cambio en los datos del que no queda constancia de a qué transacción pertenece, y no se sabría si deshacerlo.

Escribir el registro es barato (secuencial, al final del archivo); escribir las páginas es caro (aleatorio). Por eso el motor confirma la transacción cuando el registro está a salvo y aplaza las páginas.

ARIES: las tres fases

Mohan et al. (1992) definen el algoritmo que implementan casi todos los motores. Cada registro lleva un LSN (número de secuencia), y cada página guarda el LSN del último cambio que la afectó.

flowchart LR
    C["Caída"] --> A["1. ANÁLISIS<br/>desde el último punto de control:<br/>qué transacciones estaban activas<br/>y qué páginas sucias había"]
    A --> R["2. REHACER<br/>reaplicar TODO lo registrado,<br/>incluso de transacciones no confirmadas<br/>→ estado exacto del momento de la caída"]
    R --> U["3. DESHACER<br/>revertir las transacciones<br/>que no llegaron a confirmar"]
    U --> OK["Base consistente"]

El punto que sorprende: la fase de rehacer reaplica también cambios de transacciones que no se confirmaron. Se llama repeating history. Reconstruye el estado exacto del instante de la caída y solo después deshace lo que corresponde. Es más simple y más robusto que intentar filtrar durante el rehacer.

Los registros de compensación (CLR) registran el propio deshacer, de forma que si el sistema cae durante la recuperación, la siguiente no repite trabajo ya deshecho. Es lo que hace la recuperación idempotente.

Puntos de control

Sin ellos, la recuperación tendría que leer el registro desde el principio de los tiempos. Un punto de control anota el estado en un instante; la recuperación empieza ahí.

Parámetro Efecto de aumentarlo
Intervalo entre puntos de control Menos E/S en marcha, recuperación más larga
Tamaño máximo del registro Ídem

Es un compromiso explícito entre rendimiento normal y tiempo de recuperación (RTO, clase 048). Un sistema con puntos de control cada hora puede tardar mucho en volver.

fsync: durabilidad frente a qué

Escribir en un archivo no lo pone en el disco: lo deja en la caché del sistema operativo. fsync fuerza el volcado.

Configuración Caída del proceso Caída de la máquina
Sin fsync en el commit Sobrevive Se pierden transacciones confirmadas
Con fsync en el commit Sobrevive Sobrevive
fsync + disco que miente sobre su caché Sobrevive Puede perderse o corromperse

La tercera fila es real: discos de consumo con caché de escritura sin respaldo de energía confirman fsync antes de haber escrito. Es la razón por la que el hardware de servidor lleva caché con batería.

Motor Parámetro Valor seguro
PostgreSQL synchronous_commit on
PostgreSQL fsync on (nunca off en producción)
MySQL innodb_flush_log_at_trx_commit 1
SQLite synchronous FULL, o NORMAL con WAL

WAL en SQLite

SQLite es lo bastante pequeño para leer su mecanismo completo. En modo WAL:

De ahí que synchronous = NORMAL sea seguro con WAL frente a la caída del proceso pero no frente a un corte de energía: el WAL no se sincroniza en cada commit, solo en los checkpoints.

Ejemplo trabajado

Traza de una transacción y su recuperación.

LSN  Registro
100  BEGIN T1
101  T1: UPDATE cuentas id=1  saldo 1000 -> 700   [undo: 1000] [redo: 700]
102  BEGIN T2
103  T2: UPDATE cuentas id=2  saldo  500 -> 800   [undo: 500]  [redo: 800]
104  T1: UPDATE cuentas id=2  saldo  800 -> 900   (espera: fila bloqueada por T2)
105  COMMIT T2                      <- fsync aquí
106  CHECKPOINT (activas: T1)
107  T1: UPDATE cuentas id=2  saldo  800 -> 900   [undo: 800] [redo: 900]
     *** CAÍDA ***   T1 nunca confirmó

Recuperación:

ANÁLISIS   desde LSN 106: T1 activa, sin COMMIT. Páginas sucias: cuentas(1), cuentas(2)
REHACER    reaplica 107 (y 101, 103 si sus páginas no estaban en disco)
           estado tras rehacer: id=1 -> 700, id=2 -> 900     (incluye cambios de T1)
DESHACER   T1 no confirmó: revertir 107 y 101 usando la información de deshacer
           107 deshecho -> id=2 vuelve a 800   (CLR con LSN 108)
           101 deshecho -> id=1 vuelve a 1000  (CLR con LSN 109)

Estado final: id=1 → 1000 (T1 revertida), id=2 → 800 (T2 confirmada y conservada). Exactamente lo que ACID promete: T2 duradera, T1 como si nunca hubiera ocurrido.

Obsérvese que la fase de rehacer dejó momentáneamente id=2 = 900, un valor de una transacción no confirmada. No es un error: es el estado del instante de la caída, que la fase de deshacer corrige.

Comprobación en SQLite:

python - <<'PY'
import sqlite3, os
con = sqlite3.connect('demo.db')
con.execute('PRAGMA journal_mode=WAL')
con.execute('CREATE TABLE IF NOT EXISTS cuentas(id INTEGER PRIMARY KEY, saldo INTEGER)')
con.execute('INSERT OR REPLACE INTO cuentas VALUES (1, 1000)')
con.commit()
con.execute('BEGIN')
con.execute('UPDATE cuentas SET saldo = 700 WHERE id = 1')
print('WAL existe:', os.path.exists('demo.db-wal'))   # los cambios están en el WAL
os._exit(1)                                            # muerte del proceso, sin COMMIT
PY
sqlite3 demo.db "SELECT saldo FROM cuentas WHERE id=1;"   # 1000: la transacción se revirtió

El archivo -wal es visible durante la transacción y la reapertura ejecuta la recuperación automáticamente.

Comparación

Fallo Qué lo cubre
Reversión de una transacción Registro de deshacer, en marcha
Caída del proceso Registro en la caché del sistema operativo
Caída de la máquina fsync del registro en el commit
Corrupción de página Suma de comprobación + copia + registro
Pérdida del disco Réplica o copia externa (clase 048)
Error humano Recuperación a un punto en el tiempo

Errores frecuentes

  1. fsync = off en producción. Se descubre en el primer corte de energía.
  2. Puntos de control muy espaciados sin medir el RTO. La recuperación tarda más de lo que el negocio tolera.
  3. Confundir el registro del motor con el registro de la aplicación. Son cosas distintas con propósitos distintos.
  4. No vigilar el espacio del WAL. Una réplica detenida impide reciclarlo y llena el disco.
  5. Suponer que el WAL es una copia de seguridad. Lo es solo junto con una copia base.
  6. Almacenamiento que miente sobre fsync. La durabilidad depende del hardware.

De la clase a la operación

El WAL es también la base de la replicación (clase 043) y de la captura de cambios (clase 056): el mismo flujo que permite recuperarse permite que otro nodo reproduzca los cambios. Entenderlo aquí ahorra explicarlo tres veces más adelante.

Reto de transferencia

  1. Reproduce el experimento de SQLite y observa los archivos -wal y -shm.
  2. Mata el proceso a mitad de una transacción y demuestra que la base queda consistente.
  3. Mide el tiempo de recuperación con dos intervalos de punto de control distintos.
  4. Documenta la configuración de durabilidad de tu sistema y qué se pierde en cada tipo de fallo.

Preguntas de evaluación

  1. ¿Por qué el registro debe llegar al disco antes que la página de datos?
  2. Explica por qué la fase de rehacer reaplica transacciones que no se confirmaron.
  3. ¿Qué función cumplen los registros de compensación si el sistema cae durante la recuperación?
  4. Con synchronous_commit = off, ¿qué se pierde exactamente y ante qué fallo?

🌐 El mismo problema en cada motor

Caso: Qué se escribe primero para que un corte de luz no pierda nada

La durabilidad no se consigue escribiendo los datos: se consigue escribiendo la intención antes que los datos. Esa es la regla del registro anticipado: el cambio se anota en un registro secuencial y se fuerza a disco antes de tocar las páginas de datos, que pueden esperar. Al arrancar tras una caída, el motor rehace lo confirmado y deshace lo que no llegó a confirmarse. ARIES puso nombre a ese protocolo en 1992 y sigue siendo el esquema de casi todos.

Lo que hay que comparar aquí no es si cada motor lo tiene —lo tienen casi todos— sino el parámetro con el que se cambia durabilidad por rendimiento. Ese parámetro existe en todos y casi nadie lo declara al decir «nuestra base de datos es ACID».

Esta comparación es conceptual: la decisión no se reduce a una consulta con resultado, así que aquí no hay sello de máquina. Lo que se compara es lo que cada motor ofrece y a qué precio, con la página oficial al lado de cada afirmación.

Motor ¿Resuelve el caso? Nivel de prueba Código Fuente
PostgreSQL conceptual doc oficial
MySQL conceptual doc oficial
SQLite conceptual doc oficial
Redis conceptual doc oficial
MongoDB conceptual doc oficial
Apache Cassandra conceptual doc oficial
DuckDB no doc oficial

Los que resuelven el caso

PostgreSQL

MySQL

SQLite

Redis

MongoDB

Apache Cassandra

Los que no resuelven este caso — y qué se hace en su lugar

Descartar un motor con un argumento es tan formativo como usarlo. Ninguna de estas filas dice que el motor sea peor: dice que este problema no es el suyo.

Motor Por qué no Qué se hace en su lugar Fuente
DuckDB Tiene registro anticipado sobre su archivo, pero compararlo aquí no enseña nada nuevo: no hay réplica que alimentar con ese registro, ni recuperación a un punto en el tiempo, ni parámetros de durabilidad que decidir. En analítica la recuperación no se hace con el registro: se hace reconstruyendo desde el origen, que es la copia de verdad. Se trata en la parte de integración. doc

Laboratorio

python scripts/validate_repository.py
python labs/03-transactions/run_transactions_lab.py

Guarda como evidencia la salida completa, la versión del motor y la semilla o los parámetros usados. Una captura sin comando no es evidencia: no se puede repetir.

Evaluación

Criterio Peso Qué se comprueba
Comprensión conceptual 25 % Explica el mecanismo, no solo el resultado
Ejecución reproducible 25 % Otra persona obtiene lo mismo con las instrucciones dadas
Interpretación basada en evidencia 25 % Cada conclusión se apoya en una salida o una medición
Límites y riesgos declarados 25 % Dice qué no demuestra el ejercicio y qué faltaría en producción

La clase se da por superada cuando la respuesta explica el mecanismo, muestra la salida que la respalda y declara al menos un límite del ejercicio.

Fuentes de esta clase

Todo lo afirmado arriba procede de estas obras. Los identificadores viven en catalog/sources.json y el estado de los enlaces se comprueba con python scripts/check_external_links.py.


Programa · Parte 08 · ← Anterior · Siguiente →