Lab 14: Point-in-Time Recovery

Time: 45 minutes | Level: Advanced | DB: MySQL 8.0, PostgreSQL 15

Overview

Point-in-time recovery allows you to restore a database to any moment in its history — critical for recovering from accidental data deletion or corruption. MySQL uses binary logs; PostgreSQL uses WAL archiving. This lab demonstrates full PITR workflows.


Step 1: MySQL — Enable Binary Logging for PITR

docker run -d --name mysql-lab \
  -e MYSQL_ROOT_PASSWORD=rootpass \
  -e MYSQL_DATABASE=shopdb \
  mysql:8.0 \
  --server-id=1 \
  --log-bin=mysql-bin \
  --binlog-format=ROW \
  --expire_logs_days=7

for i in $(seq 1 30); do docker exec mysql-lab mysql -uroot -prootpass -e "SELECT 1" 2>/dev/null && break || sleep 2; done

# Verify binary logging
docker exec mysql-lab mysql -uroot -prootpass -e "
SHOW VARIABLES LIKE 'log_bin%';
SHOW VARIABLES LIKE 'binlog_format';
SHOW BINARY LOGS;
"

📸 Verified Output:


Step 2: Create Baseline Data and Full Backup

📸 Verified Output:

💡 --master-data=2 records the binary log position at backup time as a comment. This is your starting point for replaying binary logs during PITR.


Step 3: Simulate Normal Operations and Then "Accidental" Deletion

📸 Verified Output:


Step 4: MySQL PITR — Identify Recovery Point in Binary Logs

📸 Verified Output:


Step 5: MySQL PITR — Restore to Point Before Disaster

📸 Verified Output:

💡 Recovery stopped just before the disaster. Orders are back! The post-disaster UPDATE was not applied since we stopped before the DROP TABLE.


Step 6: PostgreSQL — Configure WAL Archiving for PITR

📸 Verified Output:


Step 7: PostgreSQL PITR — Create Data, Backup, Simulate Disaster

📸 Verified Output:


Step 8: Capstone — PostgreSQL PITR Restore

📸 Verified Output:


Summary

Component
MySQL
PostgreSQL

Log type

Binary log (binlog)

WAL (Write-Ahead Log)

Full backup

mysqldump --master-data=2

pg_basebackup

Backup position

Stored in dump header

Implicit from backup LSN

Log replay

mysqlbinlog --stop-datetime

restore_command + recovery_target_time

Recovery config

N/A

postgresql.conf + standby.signal

Complete recovery

pg_wal_replay_resume()

recovery_target_action=promote

Archive

expire_logs_days

archive_command

Key Takeaways

  • Always take full backups with log position--master-data=2 / pg_basebackup

  • Keep binlogs/WAL long enough — default 7 days may not be enough; size them to RPO requirements

  • Test your PITR procedure — practice before the disaster happens

  • --stop-datetime is safer than --stop-position for human-error recovery (you know the time, not the position)

  • PostgreSQL WAL archiving is more robust than MySQL binlogs for large setups — integrates with Barman, pgBackRest

Last updated