Part · Chapter

Backup, restore and replication

A refresher on the kinds of backup, how a database is rolled forward to the minute before a mistake, and how replication keeps a second server in step.

The short version

  • Two numbers first. RPO is how much data you may lose, RTO is how long you may be down.
  • Logical or physical. Dumps are portable and selective; file copies are fast on large data.
  • On Windows, use mysqldump or MySQL Shell. XtraBackup is Linux only and Enterprise Backup is paid.
  • The binary log closes the gap. Restore last night's dump, then replay binlogs to the second before the mistake.
  • A replica is not a backup. It copies the DROP TABLE too, so you keep both.
  • Schedule it, then test the restore. Task Scheduler runs the job; a quarterly drill proves it works.

RPO and RTO

RPO, the recovery point objective, is how much data the business can afford to lose in time, and a nightly dump alone gives an RPO of up to 24 hours. RTO, the recovery time objective, is how long the database may be down, and it decides whether a logical dump restores fast enough or you need a physical copy.

Remember

A backup you have never restored is not a backup. The restore is the product; the dump file is only an ingredient.

Logical or physical

A logical backup describes the data as statements or text that a server re-executes. A physical backup copies the data directory as the engine stored it.

AspectLogical backupPhysical backup
What it holdsCREATE and INSERT statements, or DDL plus text data filesThe data directory's files, byte for byte
Speed to takeSlow on big databases: every row is read and rendered as textFast: bounded by disk copy speed
Speed to restoreSlowest part: the server re-executes everything and rebuilds every indexFast: copy the files back, start the service
Restore granularityOne table, one schema, even one row after editing the fileUsually the whole instance
Cross-version portabilityGood: a dump from 8.4 loads into 9.7Poor: same engine, same series, same platform

Rule of thumb: logical for small and medium databases and for "bring back one table", physical when the database is too large for the RTO to survive re-executing every INSERT.

The MySQL tools on Windows

Your data sits under C:\ProgramData\MySQL\MySQL Server 8.4\Data (or 9.7), as you saw in chapter 4, and nothing there is safe to copy while the service runs.

ToolWhat it gives you
mysqldumpThe classic logical dump; --single-transaction takes one consistent InnoDB snapshot without locks, and --routines --events adds the stored programs that are skipped by default.
MySQL Shell util.dumpInstance()The modern recommendation: a folder of DDL plus chunked, compressed data, written by several threads in parallel.
MySQL Shell util.loadDump()Restores any Shell dump in parallel, resumes if interrupted, and needs local_infile=ON on the target.
The binary logEvery committed change, in order; this is what makes point-in-time recovery possible.
The Clone pluginCLONE LOCAL DATA DIRECTORY writes a free, consistent physical copy of InnoDB data while the server runs, and CLONE INSTANCE FROM seeds a replica over the network.
mysqlpumpDeprecated in 8.0.34 and removed in 8.4, so a script that still calls it has stopped running.
Percona XtraBackupFree hot physical backups, on Linux only; there is no Windows build.
MySQL Enterprise BackupHot, incremental, encrypted physical backups, and a paid part of Enterprise Edition.

Careful

Copying the data directory of a running server is not a backup: InnoDB writes pages in the background, so a copy taken over several minutes mixes moments and may refuse to start or silently lose rows. Stop the service, use the Clone plugin, or take a snapshot.

Take a dump and restore it

  1. Check that the tables are InnoDB.

    --single-transaction only guarantees consistency for InnoDB, so check the engines before you trust the dump.

    Illustration · MySQL
    SELECT engine, COUNT(*) FROM information_schema.tables
    WHERE table_schema = 'shop' GROUP BY engine;
  2. Take a consistent dump.

    Run it while nobody runs ALTER or DROP, and confirm the SOURCE_LOG_POS comment written by --source-data=2 is at the top of the file.

    PowerShell
    # the backtick continues the line
    mysqldump -u root -p --single-transaction --routines --events --triggers `
      --source-data=2 --databases shop `
      --result-file=D:\backups\shop-2026-09-27.sql
  3. Restore it into a fresh database.

    Never test on production. PowerShell reserves the < operator, so load the file from cmd.exe or with the client's source command.

    Illustration · MySQL
    CREATE DATABASE shop_restore;
    -- inside the mysql client (forward slashes are fine):
    source D:/backups/shop-2026-09-27.sql
    -- or from cmd.exe:  mysql -u root -p shop_restore < D:\backups\shop.sql
  4. Verify.

    Compare row counts and checksums with the source, then run one real query the application uses.

    Illustration · MySQL
    SELECT COUNT(*) FROM shop_restore.orders;
    CHECKSUM TABLE shop.orders, shop_restore.orders;   -- both should match
  5. Schedule it on Windows.

    There is no cron. Task Scheduler (or schtasks) runs a .ps1 nightly, and mysql_config_editor keeps the password out of the script in %APPDATA%\MySQL\.mylogin.cnf.

    PowerShell
    # once: store credentials for a dedicated backup account
    mysql_config_editor set --login-path=backup --host=localhost --user=backup --password
    
    # backup-shop.ps1
    $stamp = Get-Date -Format 'yyyy-MM-dd'
    & "C:\Program Files\MySQL\MySQL Server 8.4\bin\mysqldump.exe" --login-path=backup `
      --single-transaction --routines --events --triggers --source-data=2 `
      --databases shop --result-file="D:\backups\shop-$stamp.sql"
    
    # register it
    schtasks /Create /SC DAILY /ST 02:00 /TN "shop backup" /TR "powershell -NoProfile -File D:\backups\backup-shop.ps1"
  6. Keep a copy off the machine.

    A backup on the same disk as the database dies with the disk. Copy dumps and binary logs to a share or cloud storage.

Point-in-time recovery with the binary log

A nightly dump restores last night, and the binary log carries you forward from there; log_bin is ON by default in 8.x and later. Say a colleague ran UPDATE orders SET status = 'cancelled' at 10:42 and forgot the WHERE: restore last night's dump into a scratch server, read SOURCE_LOG_POS from the top of the dump, and replay the binary logs from that position with mysqlbinlog, stopping one second before the damage. Send all the files through one mysql process so a transaction that spans two files stays intact. This works only while every binary log since the dump still exists, so copy them off the machine with the dumps.

Illustration · MySQL
REM cmd.exe: replay from the dump's position, stop just before the mistake
mysqlbinlog --start-position=157 --stop-datetime="2026-09-27 10:41:59" ^
  "C:\ProgramData\MySQL\MySQL Server 8.4\Data\binlog.000041" ^
  "C:\ProgramData\MySQL\MySQL Server 8.4\Data\binlog.000042" | mysql -u root -p

For you as a DBA

"Can you get back the orders from Tuesday?" depends on three things you should know cold: when the last full backup ran, whether the binary logs since then still exist, and how long a restore takes. Give the backup job its own account with only the privileges it needs (chapter 14), never root.

Ask the AI

"MySQL 8.4 on Windows 11: write a PowerShell script that runs mysqldump with --single-transaction, --routines and --events for the database shop using the login path named backup, writes to D:\backups with today's date in the file name, and give me the schtasks command to run it daily at 02:00."

The PostgreSQL equivalents

PostgreSQL The same shapes under different names. Roles and tablespaces live outside any single database, so a complete logical backup is pg_dumpall --globals-only plus one pg_dump per database.

ToolWhat it gives you
pg_dumpLogical dump of one database without blocking readers or writers; -Fc is compressed, -Fd dumps in parallel.
pg_dumpallPlain SQL for the whole cluster, and the only way to get roles and tablespaces.
pg_restoreRestores custom and directory dumps, in parallel, down to a single table.
pg_basebackupPhysical copy of a running cluster you can start; block-level incremental since 17, and how a standby is born.
WAL archivingarchive_mode plus archive_command keeps every finished segment, which a restore_command and recovery_target_time replay for PITR.
pgBackRestManages full and incremental backups, archiving, retention and restores in one place, on Linux only.

Replication

MySQL You replicate for read scaling, for high availability, and for an always-current offsite copy. MySQL ships binary log events from a source to a replica, asynchronous by default, so a crash can lose the last transactions that never arrived; semisynchronous replication makes COMMIT wait for one replica. Group Replication elects a new primary on its own, and through MySQL Shell's AdminAPI it becomes an InnoDB Cluster. Check lag with SHOW REPLICA STATUS\G: both threads running with Seconds_Behind_Source climbing means the replica cannot keep up, so reports there show stale numbers.

PostgreSQL A PostgreSQL replica is a standby. Streaming replication ships WAL as the primary writes it, copying the whole cluster at the same major version, and a hot standby serves read-only queries. Logical replication is the selective kind: a publication names tables, a subscription pulls their row changes, and the target may run a different major version. Either way a replica is not a backup: it replicates the DROP TABLE too, a few milliseconds later.

Illustration · MySQL
CHANGE REPLICATION SOURCE TO SOURCE_HOST = 'db1', SOURCE_USER = 'repl',
  SOURCE_PASSWORD = '...', SOURCE_AUTO_POSITION = 1;
START REPLICA;
SHOW REPLICA STATUS\G      -- the health check you will run most

Managed cloud databases

Both On Amazon RDS, Google Cloud SQL or Azure Database, the provider takes the backups and offers point-in-time restore and one-click replicas, which removes the scripting and none of your responsibility for retention, restore drills and lag.

Where to go deeper

Quick check

1. The shop's owner says "we can afford to lose at most fifteen minutes of orders, and we can be down for two hours." Which is which?

RPO is how much data you may lose (fifteen minutes, so nightly dumps alone are not enough and you need the binary log); RTO is how long you may be down (two hours). Reread "RPO and RTO".

2. A colleague copies C:\ProgramData\MySQL\MySQL Server 8.4\Data to a USB stick every Friday while the service is running and calls it the backup. What is wrong?

InnoDB writes pages in the background and keeps recent changes in the redo log, so a running data directory is not a consistent snapshot. Stop the service, use the Clone plugin, or take a logical dump. Reread "The MySQL tools on Windows".

3. You restore last night's plain mysqldump of shop and the application's stored procedures are gone. Why?

Triggers are dumped by default, but routines and events are not; add --routines --events to every backup script. Reread the mysqldump row in "The MySQL tools on Windows".

4. Someone ran an UPDATE without a WHERE clause at 10:42. The last full dump ran at 02:00 with --source-data=2. What is the correct recovery order?

Full backup first, then roll forward with the binary log to just before the mistake. Binary logs cannot be replayed backwards, and a replica applied the same bad UPDATE moments later. Reread "Point-in-time recovery with the binary log".

5. On a MySQL 9.7 replica, SHOW REPLICA STATUS shows Replica_IO_Running Yes, Replica_SQL_Running Yes and Seconds_Behind_Source climbing from 0 to 900 during the nightly batch job. What is happening?

Both threads running with a rising Seconds_Behind_Source is replication lag, the everyday replication problem; it usually recovers once the batch ends, and reports run on the replica meanwhile show old numbers. Reread "Replication".