Salta el contingut

Restauració

El cicle complet: backup + restauració

Un backup que mai s'ha provat és un backup del qual no és pot fiar. La prova de restauració és part obligatòria de qualsevol pla de backup seriós. Els escenaris reals on cal restaurar una BD son:

  • Corrupció de dades: Una aplicació ha inserit dades incorrectes i cal tornar a un estat anterior.
  • Error humà: Un DROP TABLE o DELETE accidental ha elimitat dades importants.
  • Migració de servidor: S'ha de moure la BD a un nou servidor.
  • Creació d'un entorn de prova: Cal una còpia de les dades de producció per a proves.
  • Desastre: El servidor ha mort i cal restaurar des de zero.

Restauració amb pg_restore (PostgreSQL)

Des d'un fitxer SQL

# Restaurar des d'un fitxer SQL (format --format=plain o per defecte)
psql -h localhost -U gbd_user -d gbd_nom_cognom < backup_20251001.sql

# Amb Docker
docker exec -i postgres-nom-cognom psql -U gbd_user -d gbd_nom_cognom < backup_20251001.sql

Des d'un format binari (pg_restore)

# Restaurar el backup binari complet
pg_restore -h localhost -U gbd_user -d gbd_nom_cognom backup_20251001.dump

# Restaurar només una taula específica
pg_restore -h localhost -U gbd_user -d gbd_nom_cognom -t clients backup_20251001.dump

# Restaurar a una BD nova (crear la BD primer)
createdb -U postgres -O gbd_user gbd_test_restore
pg_restore -h localhost -U gbd_user -d gbd_test_restore backup_20251001.dump

# Amb Docker
docker exec -i postgres-nom-cognom pg_restore -U gbd_user -d gbd_nom_cognom < backup_20251001.dump

Procediment de restauració completa

#!/bin/bash
# restore_postgresql.sh

CONTAINER="postgres-nom-cognom"
DB="gbd_nom_cognom"
USER="gbd_user"
BACKUP_FILE="$1"  # Primer argument: ruta al backup

if [ -z "$BACKUP_FILE" ]; then
    echo "Ús: $0 <fitxer_backup>"
    exit 1
fi

echo "=== RESTAURACIÓ DE LA BASE DE DADES ==="
echo "BD: $DB"
echo "Fitxer: $BACKUP_FILE"
echo "Data: $(date)"

# 1. Desconnectar tots els usuaris de la BD
docker exec "$CONTAINER" psql -U postgres -c "
    SELECT pg_terminate_backend(pid)
    FROM pg_stat_activity
    WHERE datname = '$DB' AND pid <> pg_backend_pid();"

# 2. Eliminar la BD existent (si cal)
docker exec "$CONTAINER" psql -U postgres -c "DROP DATABASE IF EXISTS ${DB}_old;"
docker exec "$CONTAINER" psql -U postgres -c "ALTER DATABASE $DB RENAME TO ${DB}_old;"

# 3. Crear la BD nova
docker exec "$CONTAINER" createdb -U "$USER" "$DB"

# 4. Restaurar
docker exec -i "$CONTAINER" pg_restore -U "$USER" -d "$DB" < "$BACKUP_FILE"

if [ $? -eq 0 ]; then
    echo "Restauració completada."
    # Eliminar la BD antiga (o mantenir-la per seguretat)
    # docker exec "$CONTAINER" psql -U postgres -c "DROP DATABASE ${DB}_old;"
else
    echo "ERROR en la restauració!" >&2
    exit 1
fi

Restauració amb MySQL

# Restaurar des d'un fitxer SQL
mysql -u gbd_user -pgbd2025 gbd_nom_cognom < backup_20251001.sql

# Amb Docker
docker exec -i mysql-nom-cognom mysql -u gbd_user -pgbd2025 gbd_nom_cognom < backup_20251001.sql

# Amb progress (per a fitxers grans)
pv backup_20251001.sql | docker exec -i mysql-nom-cognom mysql -u gbd_user -pgbd2025 gbd_nom_cognom

Restauració fins a un punt en el temps (PITR)

La restauració estàndard (full backup → restore) us porta al moment en que es va fer el backup. Si necessiteu les dades tal com estaven a una hora o transacció concreta entre dos backups, cal aplicar els logs de transaccions sobre el backup, parant exactament on vulgueu. Això és el Point-In-Time Recovery (PITR).

flowchart LR
    FB["Backup complet\n(diumenge 02:00)"]
    L1["Archive logs\ndilluns"]
    L2["Archive logs\ndimarts"]
    L3["Archive logs\ndimecres\n(fins 11:42)"]
    T["Incident\ndimecres 11:47\n(DELETE accidental)"]
    R["BD restaurada\na dimecres 11:42"]

    FB --> L1 --> L2 --> L3 --> T
    FB -->|"1. Restaurar backup"| R
    L1 -->|"2. Aplicar logs"| R
    L2 -->|"2. Aplicar logs"| R
    L3 -->|"3. Parar just abans\nde l'incident"| R

El procés general té sempre tres fases:

  1. Identificar el punt objectiu — trobar la marca de temps, el número de transacció o la posició al log que correspon a just abans de l'incident.
  2. Restaurar el backup base — fer una còpia completa del backup complet a la ubicació de treball.
  3. Aplicar els logs fins al punt objectiu — el SGBD processa els archive logs en ordre, i s'atura just on li diem.

PostgreSQL: PITR amb pg_basebackup i WAL

PostgreSQL fa el PITR combinant un backup físic (pg_basebackup) amb els segments WAL arxivats. La configuració del punt de parada es fa a postgresql.conf (PostgreSQL 12+) o a recovery.conf (fins a PostgreSQL 11).

Pas 1 — Identificar el punt objectiu

Abans de restaurar, cal saber exactament on volem parar. Teniu tres opcions:

-- Opció A: per marca de temps (la més habitual)
-- Sabeu que l'incident va passar "dimecres a les 11:47"
-- Voleu restaurar fins a les 11:42

-- Opció B: per LSN (Log Sequence Number)
-- Coneixeu la posició exacta al WAL
SELECT pg_current_wal_lsn();
-- Exemple de resultat: 0/A3F20000

-- Opció C: per XID (Transaction ID)
-- Sabeu que la transacció culpable tenia XID=58291
-- Voleu recuperar fins a XID=58290

-- Com trobar el XID d'una transacció concreta (si teniu pg_audit o logs):
SELECT xmin, * FROM clients WHERE id = 999;
-- El camp xmin us diu quina transacció va inserir o modificar la fila per darrera vegada

Pas 2 — Preparar l'entorn de restauració

# Aturar PostgreSQL si s'está executant
docker stop postgres-nom-cognom

# Buidar el directori de dades (o usar un directori temporal)
# IMPORTANT: fer una còpia de seguretat del directori actual primer!
PGDATA="/var/lib/postgresql/data"
mv "$PGDATA" "${PGDATA}_original_$(date +%Y%m%d_%H%M%S)"
mkdir -p "$PGDATA"

# Restaurar el backup base (pg_basebackup)
# Si el backup és un tar:
tar -xzf /backup/pg_base_20260322.tar.gz -C "$PGDATA"

# Si el backup es va fer directament al directori:
cp -a /backup/pg_base_20260322/. "$PGDATA/"

# Assegurar els permisos correctes
chown -R postgres:postgres "$PGDATA"
chmod 700 "$PGDATA"

Pas 3 — Configurar el punt de recuperació

# postgresql.conf (PostgreSQL 12+)
# Afegir o modificar aquests paràmetres:

# Activar el mode de recuperació
restore_command = 'cp /arxiu/wal/%f %p'
# Aquesta comanda diu a PostgreSQL on trobar els segments WAL arxivats
# %f = nom del fitxer WAL, %p = ruta de destí

# --- TRIEU UNA DE LES TRES OPCIONS SEGÜENTS ---

# Opció A: fins a una marca de temps
recovery_target_time = '2026-03-25 11:42:00'
recovery_target_timezone = 'Europe/Madrid'

# Opció B: fins a un LSN
recovery_target_lsn = '0/A3F20000'

# Opció C: fins a un XID
recovery_target_xid = '58290'

# --- FI DE LES OPCIONS ---

# Acció en arribar al punt objectiu:
# 'pause'  → s'atura i espera confirmació (podeu inspeccionar l'estat)
# 'promote' → es converteix en primari immediatament
# 'shutdown' → s'atura el servidor
recovery_target_action = 'pause'

# Inclusió de la transacció objectiu (per a recovery_target_xid i recovery_target_lsn):
# true  → inclou la transacció objectiu
# false → s'atura just ABANS (útil per desfer un DELETE accidental)
recovery_target_inclusive = false
# PostgreSQL 12+: crear el fitxer senyal per indicar que s'ha de fer recovery
touch "$PGDATA/recovery.signal"
# (substitueix el recovery.conf de versions anteriors)
# PostgreSQL 11 i anteriors: crear recovery.conf dins de $PGDATA
# (en comptes de modificar postgresql.conf)
restore_command = 'cp /arxiu/wal/%f %p'
recovery_target_time = '2026-03-25 11:42:00'
recovery_target_action = 'pause'
recovery_target_inclusive = false

Pas 4 — Arrancar i seguir el progrés

# Arrancar PostgreSQL en mode recovery
docker start postgres-nom-cognom
# O: pg_ctl start -D "$PGDATA"

# Seguir el progrés als logs
docker logs -f postgres-nom-cognom
# Sortida esperada als logs durant el recovery:
LOG:  starting point-in-time recovery to 2026-03-25 11:42:00+01
LOG:  restored log file "000000010000000A00000012" from archive
LOG:  redo starts at 0/A1234000
LOG:  consistent recovery state reached at 0/A1240000
LOG:  recovery stopping before commit of transaction 58291, time 2026-03-25 11:47:23+01
LOG:  pausing at the end of recovery
HINT: Execute pg_wal_replay_resume() to promote.

Pas 5 — Verificar i confirmar la recuperació

-- Connectar-se (en mode pause el servidor accepta connexions de lectura)
-- Verificar que les dades són correctes
SELECT COUNT(*) FROM clients;
SELECT * FROM clients ORDER BY id DESC LIMIT 10;

-- Si les dades semblen correctes: confirmar la recuperació i fer el servidor primari
SELECT pg_wal_replay_resume();

-- Si les dades NO són les esperades: aturar i ajustar recovery_target_time
-- (o usar recovery_target_inclusive = true per incloure una transacció més)

pg_waldump: inspeccionar el contingut del WAL

Si no sabeu exactament quan va passar l'incident, podeu llegir el contingut dels segments WAL:

pg_waldump /arxiu/wal/000000010000000A00000015 | grep -i "delete\|drop"
# Mostra totes les operacions DELETE i DROP amb el seu timestamp i XID


MySQL / MariaDB: PITR amb binary log

MySQL no té un mecanisme de backup físic en calent integrat a la versió Community. El PITR es fa restaurant un mysqldump complet i aplicant els binary logs posteriors amb mysqlbinlog.

Pas 1 — Identificar la posició objectiu al binary log

# Llistar els binary logs disponibles al servidor (o a la carpeta d'arxiu)
ls -lh /var/lib/mysql/binlog.*
# o dins del contenidor:
docker exec mysql-nom-cognom ls -lh /var/lib/mysql/binlog.*
-- Veure tots els binary logs i la seva mida
SHOW BINARY LOGS;

-- Veure els esdeveniments d'un binary log concret
-- Busquem l'operació problemàtica (ex: un DELETE accidental)
SHOW BINLOG EVENTS IN 'binlog.000042' \G
# mysqlbinlog: llegir el contingut del binary log en format llegible
docker exec mysql-nom-cognom mysqlbinlog \
    --base64-output=DECODE-ROWS \
    --verbose \
    /var/lib/mysql/binlog.000042 | grep -A 10 "DELETE FROM clients"
# Sortida típica de mysqlbinlog (busqueu el timestamp i la posició):
# at 487291
#260325 11:47:23 server id 1  end_log_pos 487512  Query   thread_id=42
#                              ^^^^^^^^^^^
#                              Posició al log: 487291 (inici) fins a 487512 (fi)
#                              Timestamp: 2026-03-25 11:47:23
DELETE FROM clients WHERE segment = 'premium'

Apunteu-vos la posició d'inici de l'operació problemàtica (en el exemple: 487291). La restauració s'ha de parar just abans d'aquesta posició, és a dir, fins a la posició 487290.

Pas 2 — Restaurar el backup complet

# Aturar les aplicacions que usen la BD (per evitar escriptures noves)
# Restaurar el backup mysqldump base
docker exec -i mysql-nom-cognom mysql -u root -proot2025 <<EOF
DROP DATABASE IF EXISTS gbd_nom_cognom;
CREATE DATABASE gbd_nom_cognom CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
EOF

docker exec -i mysql-nom-cognom mysql -u root -proot2025 gbd_nom_cognom \
    < /backup/gbd_nom_cognom_20260322_020000.sql

echo "Backup base restaurat. Ara aplicarem els binary logs..."

Pas 3 — Aplicar els binary logs fins al punt objectiu

# Opció A: fins a una marca de temps
# Aplica tots els canvis des del backup fins a les 11:42:00
docker exec mysql-nom-cognom mysqlbinlog \
    --start-datetime="2026-03-22 02:00:01" \
    --stop-datetime="2026-03-25 11:42:00" \
    /var/lib/mysql/binlog.000039 \
    /var/lib/mysql/binlog.000040 \
    /var/lib/mysql/binlog.000041 \
    /var/lib/mysql/binlog.000042 \
    | docker exec -i mysql-nom-cognom mysql -u root -proot2025 gbd_nom_cognom
# Opció B: fins a una posició exacta al binary log (més precís)
# Aplica tot fins a la posició 487290 del fitxer binlog.000042
# (la posició 487291 és el DELETE que volem evitar)

# Primer: aplicar tots els logs anteriors complets
docker exec mysql-nom-cognom mysqlbinlog \
    --start-datetime="2026-03-22 02:00:01" \
    /var/lib/mysql/binlog.000039 \
    /var/lib/mysql/binlog.000040 \
    /var/lib/mysql/binlog.000041 \
    | docker exec -i mysql-nom-cognom mysql -u root -proot2025 gbd_nom_cognom

# Després: aplicar el darrer log fins a la posició exacta
docker exec mysql-nom-cognom mysqlbinlog \
    --stop-position=487290 \
    /var/lib/mysql/binlog.000042 \
    | docker exec -i mysql-nom-cognom mysql -u root -proot2025 gbd_nom_cognom

Pas 4 — Verificar

-- Connectar-se i verificar que les dades estan en l'estat esperat
SELECT COUNT(*) FROM clients WHERE segment = 'premium';
-- Ha de retornar el nombre de clients 'premium' que hi havia ABANS del DELETE

-- Comprovació addicional: veure la darrera transacció aplicada
SHOW MASTER STATUS;

Ordre dels binary logs

Si l'operació problemàtica i els logs que voleu aplicar abracen múltiples fitxers binlog, heu de passar-los tots a mysqlbinlog en una sola comanda (com al pas 3). Si els apliqueu en comandes separades, MySQL pot detectar discontinuïtats i rebutjar aplicar-los.


SQL Server: PITR amb RESTORE LOG

SQL Server integra el PITR directament al seu motor de backup. Amb el model de recuperació FULL, la seqüència és: restaurar el backup complet en mode NORECOVERY, aplicar opcionalment els backups diferencials, i finalment aplicar els backups del transaction log amb STOPAT.

Pas 1 (SQL Server) — Identificar el punt objectiu

-- Veure la cadena de backups disponible per a una BD
SELECT
    backup_start_date,
    backup_finish_date,
    type,                  -- D=Database, I=Differential, L=Log
    database_name,
    first_lsn,
    last_lsn,
    physical_device_name
FROM msdb.dbo.backupset bs
JOIN msdb.dbo.backupmediafamily bmf ON bs.media_set_id = bmf.media_set_id
WHERE database_name = 'gbd_practica'
ORDER BY backup_start_date DESC;
-- Si coneixeu la transacció problemàtica, busqueu-la al transaction log
-- (requereix que el fitxer .trn del log estigui disponible)
-- Primer, restaurar el log en un entorn de test per inspeccionar-lo
RESTORE LOG gbd_practica_test
FROM DISK = '/var/opt/mssql/backup/gbd_log_20260325_1200.trn'
WITH STOPBEFOREMARK = 'BEGIN_TRAN accidental_delete';
-- (necessita que la transacció estigui marcada amb BEGIN TRAN amb nom)

Pas 2 — Restaurar el backup complet en mode NORECOVERY

-- Mode NORECOVERY: la BD queda en estat "Restoring..."
-- i accepta més operacions de RESTORE posteriors
RESTORE DATABASE gbd_practica
FROM DISK = '/var/opt/mssql/backup/gbd_practica_full_20260322.bak'
WITH
    NORECOVERY,
    MOVE 'gbd_practica'      TO '/var/opt/mssql/data/gbd_practica.mdf',
    MOVE 'gbd_practica_log'  TO '/var/opt/mssql/data/gbd_practica_log.ldf',
    STATS = 10;
-- MOVE és necessari si la ruta del fitxer .mdf/.ldf és diferent a l'original

Pas 3 — Aplicar el backup diferencial (si n'hi ha)

-- Si teniu un backup diferencial entre el full i els logs, apliqueu-lo primer
-- Continueu en mode NORECOVERY
RESTORE DATABASE gbd_practica
FROM DISK = '/var/opt/mssql/backup/gbd_practica_diff_20260325.bak'
WITH NORECOVERY, STATS = 10;

Pas 4 — Aplicar els backups de log fins al punt objectiu

-- Opció A: fins a una marca de temps
-- Apliqueu els fitxers .trn en ordre cronològic
-- Tots en NORECOVERY excepte el darrer

RESTORE LOG gbd_practica
FROM DISK = '/var/opt/mssql/backup/gbd_log_20260325_0600.trn'
WITH NORECOVERY;

RESTORE LOG gbd_practica
FROM DISK = '/var/opt/mssql/backup/gbd_log_20260325_0700.trn'
WITH NORECOVERY;

-- ... (un RESTORE LOG per cada fitxer .trn fins arribar a l'hora objectiu)

-- Darrer fitxer: amb STOPAT per aturar-se al minut exacte
RESTORE LOG gbd_practica
FROM DISK = '/var/opt/mssql/backup/gbd_log_20260325_1200.trn'
WITH
    STOPAT = '2026-03-25 11:42:00',
    RECOVERY;
-- RECOVERY tanca la seqüència: la BD passa d'estat "Restoring" a "Online"
-- Opció B: fins a una transacció marcada (named transaction)
-- (el codi de l'aplicació hauria d'haver marcat la transacció)
-- BEGIN TRANSACTION inici_carrega WITH MARK 'càrrega massiva clients'

RESTORE LOG gbd_practica
FROM DISK = '/var/opt/mssql/backup/gbd_log_20260325_1200.trn'
WITH
    STOPATMARK = 'inici_carrega',     -- inclou la transacció marcada
    -- STOPBEFOREMARK = 'inici_carrega',  -- s'atura JUST ABANS
    RECOVERY;

Pas 5 — Verificar que la BD és accessible

-- La BD ha de ser accessible (estat Online)
SELECT name, state_desc FROM sys.databases WHERE name = 'gbd_practica';

-- Verificar les dades
USE gbd_practica;
SELECT COUNT(*) FROM clients;

-- Veure les darreres transaccions aplicades
SELECT TOP 10
    operation,
    context,
    transaction_id,
    [begin time]
FROM fn_dblog(NULL, NULL)
ORDER BY [Current LSN] DESC;

La cadena de logs no es pot trencar

Cada backup de log conté el rang de LSN que cobreix. Si us falta un fitxer .trn intermedi, SQL Server no podrà continuar la restauració i retornarà un error. Guardeu tots els fitxers .trn en ordre i verifiqueu que la cadena és completa amb RESTORE HEADERONLY.

-- Verificar el contingut d'un fitxer de backup
RESTORE HEADERONLY FROM DISK = '/var/opt/mssql/backup/gbd_log_20260325_1200.trn';
-- Comproveu que FirstLSN del fitxer nou = LastLSN del fitxer anterior

Oracle: PITR amb RMAN

Oracle RMAN és l'eina més completa per a PITR. Permet restaurar fins a un SCN, fins a un timestamp, fins a una seqüència de redo log o fins a una transacció concreta (Flashback Transaction, si està activada).

Pas 1 (Oracle) — Identificar el punt objectiu

-- Opció A: trobar el SCN associat a una marca de temps
SELECT timestamp_to_scn(TIMESTAMP '2026-03-25 11:42:00') FROM dual;
-- Resultat: ex. 8472930

-- Opció B: trobar el SCN de l'última operació "bona" a una taula
-- (requereix Flashback Query, que necessita UNDO retent prou temps)
SELECT versions_xid, versions_operation, versions_startscn, versions_endscn,
       nom, segment
FROM clients VERSIONS BETWEEN SCN MINVALUE AND MAXVALUE
WHERE segment = 'premium'
ORDER BY versions_startscn DESC;
-- Busqueu l'entrada DELETE i preneu el versions_startscn - 1 com a objectiu

-- Opció C: trobar la seqüència del redo log en el moment desitjat
SELECT sequence#, first_time, next_time
FROM v$archived_log
WHERE first_time <= TIMESTAMP '2026-03-25 11:42:00'
  AND next_time  >= TIMESTAMP '2026-03-25 11:42:00'
  AND standby_dest = 'NO';

Pas 2 — Connectar a RMAN i restaurar

La restauració completa d'Oracle requereix que la BD estigui en estat MOUNT (no OPEN). RMAN gestiona tot el procés: restaura els datafiles del backup i aplica els archive logs fins al punt especificat.

# Connectar a RMAN
rman target /

# Verificar els backups disponibles
RMAN> LIST BACKUP SUMMARY;
RMAN> LIST ARCHIVELOG ALL;
# Opció A: PITR fins a un timestamp
RMAN> SHUTDOWN IMMEDIATE;
RMAN> STARTUP MOUNT;

RMAN> RUN {
    SET UNTIL TIME "TO_DATE('2026-03-25 11:42:00','YYYY-MM-DD HH24:MI:SS')";
    RESTORE DATABASE;
    RECOVER DATABASE;
}

# Obrir la BD amb RESETLOGS (obligatori després de PITR)
RMAN> ALTER DATABASE OPEN RESETLOGS;
# Opció B: PITR fins a un SCN concret
RMAN> SHUTDOWN IMMEDIATE;
RMAN> STARTUP MOUNT;

RMAN> RUN {
    SET UNTIL SCN 8472930;
    RESTORE DATABASE;
    RECOVER DATABASE;
}

RMAN> ALTER DATABASE OPEN RESETLOGS;
# Opció C: PITR fins a una seqüència de redo log concreta
RMAN> RUN {
    SET UNTIL SEQUENCE 1142 THREAD 1;
    RESTORE DATABASE;
    RECOVER DATABASE;
}
RMAN> ALTER DATABASE OPEN RESETLOGS;
# Opció D: PITR d'un tablespace concret (sense aturar tota la BD)
# (Tablespace Point-In-Time Recovery - TSPITR)
RMAN> RECOVER TABLESPACE users
    UNTIL TIME "TO_DATE('2026-03-25 11:42:00','YYYY-MM-DD HH24:MI:SS')"
    AUXILIARY DESTINATION '/tmp/tspitr_aux';

Pas 3 — Verificar i sincronitzar seqüències

-- Verificar l'estat de la BD després del RESETLOGS
SELECT name, open_mode, log_mode, resetlogs_time FROM v$database;

-- Verificar que les dades s'han restaurat correctament
SELECT COUNT(*) FROM clients WHERE segment = 'premium';

-- IMPORTANT: després d'un RESETLOGS, les seqüències Oracle poden haver retrocès.
-- Comprovar totes les seqüències de la BD i sincronitzar-les amb el màxim valor real
SELECT sequence_name, last_number FROM user_sequences;

-- Resincronitzar una seqüència si cal
-- (substituïu 9999 pel valor màxim actual de la clau primària + marge)
ALTER SEQUENCE seq_clients INCREMENT BY 9999;
SELECT seq_clients.NEXTVAL FROM dual;
ALTER SEQUENCE seq_clients INCREMENT BY 1;

Flashback Database: alternativa ràpida a PITR a Oracle

Si teniu Flashback Database activat (ALTER DATABASE FLASHBACK ON), podeu tornar la BD enrere sense necessitat de restaurar des de backup. És molt més ràpid perquè usa els flashback logs en comptes dels archive logs:

-- Molt més ràpid que un PITR complet
SHUTDOWN IMMEDIATE;
STARTUP MOUNT;
FLASHBACK DATABASE TO TIMESTAMP(SYSTIMESTAMP - INTERVAL '30' MINUTE);
ALTER DATABASE OPEN RESETLOGS;
El prerequisit és que la Flashback Retention Area tingui espai suficient i que el canvi no sigui massa antic.


Resum comparatiu de PITR

SGBD Eina Granularitat minima Prerequisit
PostgreSQL pg_basebackup + WAL 1 transacció (per XID) wal_level=replica, archive_mode=on
MySQL mysqldump + mysqlbinlog 1 posicio al binary log log_bin activat
SQL Server BACKUP/RESTORE LOG 1 transacció marcada Model de recuperació FULL, backups regulars del log
Oracle RMAN 1 SCN (~ 1 transacció) Mode ARCHIVELOG, RMAN configurat

AC0372/06/03 — PITR a la pràctica

RA6 · CA6.4

Simuleu un incident i feu la recuperació:

  1. Feu un backup complet de la vostra BD de pràctiques.
  2. Inseriu 10 files noves a una taula (INSERT INTO clients ...). Apunteu el timestamp.
  3. Feu un DELETE FROM clients WHERE id > 1000 (simulant un error accidental). Apunteu el timestamp.
  4. Inseriu 5 files més (per simular que l'aplicació ha continuat treballant).
  5. Realitzeu un PITR per recuperar l'estat just abans del DELETE. Verifiqueu que les 10 files inserides al pas 2 hi son, i que les files del pas 3 no s'han esborrat.
  6. Documenteu cada pas amb els timestamps i posicions de log que heu utilitzat.

Conceptes: RTO i RPO

Quan es dissenya un pla de backup, dos conceptes clau defineixen els objectius de disponibilitat:

timeline
    title Cicle de vida d'un incident de dades
    Incident : Fallada del sistema o pèrdua de dades
             : RPO - fins aqui les dades són recoverables
    Detectat : L'equip detecta el problema
    Inici restauracio : S'inicia el procediment de recuperació
    Sistema operatiu : El sistema torna a estar disponible
                     : RTO - temps total de recuperació
  • RPO (Recovery Point Objective): La quantitat màxima de dades (mesurada en temps) que l'organització pot acceptar perdre. Si el RPO és de 1 hora, cal fer backups cada hora.

  • RTO (Recovery Time Objective): El temps màxim que pot estar el sistema inactiu mentres es recupera. Si el RTO és de 4 hores, tot el procediment de restauració ha de completar-se en menys de 4 hores.

Tipus d'empresa RPO típic RTO típic
Banca / borsa Quasi zero (replicació en temps real) Minuts
E-commerce 15 minuts - 1 hora 1-4 hores
Empresa mitjana 1-8 hores 4-24 hores
Empresa petita Diaria 24-48 hores

Verificació del backup

després de fer un backup, cal verificar que és vàlid:

-- Verificar el backup (pg_restore en mode --list)
pg_restore --list backup_20251001.dump | head -50

-- Restaurar a una BD temporal i fer verificacions
createdb -U postgres test_verify_backup
pg_restore -U gbd_user -d test_verify_backup backup_20251001.dump

-- Comptar les files de les taules principals
psql -U gbd_user -d test_verify_backup -c "
SELECT
    schemaname,
    tablename,
    n_live_tup AS num_files_estimades
FROM pg_stat_user_tables
ORDER BY n_live_tup DESC;"

-- Eliminar la BD de verificació
dropdb -U postgres test_verify_backup
# Restaurar a una BD de test
mysql -u root -proot2025 -e "CREATE DATABASE test_verify;"
mysql -u root -proot2025 test_verify < backup_20251001.sql

# Verificar taules
mysql -u root -proot2025 test_verify -e "SHOW TABLE STATUS;"

# Netejar
mysql -u root -proot2025 -e "DROP DATABASE test_verify;"

Mai restaureu directament sobre producció sense prova

Sempre restaureu primer en un entorn de test per verificar que el backup és vàlid i que les dades són les correctes. Nomes després, si la verificació és satisfactòria, procediu amb la restauració a producció seguint el procediment documentat.

AC0372/06/02 — Miniactivitat

RA6 · CA6.4

Realitzeu un cicle complet de backup + restauració:

  1. Feu un backup de la vostra BD de pràctiques.
  2. Creeu una BD nova gbd_nom_cognom_restore.
  3. Restaureu el backup a la nova BD.
  4. Verifiqueu que totes les taules i les dades són correctes (SELECT COUNT(*) per a cada taula).
  5. Documenteu el temps que ha trigat cada pas.