Every database eventually loses data: a mistaken DELETE, a bad deployment, a failed disk, a hacked server, a ransomware attack. A backup you have never restored is only a hope. This chapter shows real backup and recovery commands, run against real MySQL servers. This first lesson is about planning.
Two numbers decide your strategy
| Term | Question | Example |
|---|---|---|
| RPO (recovery point objective) | How much data can we afford to lose? | “At most 5 minutes of orders” |
| RTO (recovery time objective) | How long can we be down? | “Back online within 1 hour” |
A nightly dump gives an RPO of up to 24 hours: everything since the last backup is lost. To lower the RPO to minutes, you add the binary log (Point-in-Time Recovery). To lower the RTO, you need faster restores: physical backups, a standby replica, and a practised procedure (Replication Basics).
Ask the business for the numbers. “We want zero data loss and zero downtime” has a price, and people reconsider when they see it.
Logical and physical backups
Logical (mysqldump) | Physical (files / CLONE / XtraBackup / snapshots) | |
|---|---|---|
| What it holds | SQL statements that rebuild the data | A copy of the data files |
| Size | Text (compresses well) | Roughly the size of the data |
| Backup speed | Slower on large data | Fast |
| Restore speed | Slow (runs every INSERT, rebuilds indexes) | Fast (copy the files back) |
| Portable across versions and platforms | Yes | Only the same major version, mostly the same platform |
| Restore one table | Easy | Harder |
| Good for | Small and medium databases, migrations, upgrades | Large databases, short RTO |
Most teams use both: a physical (or replica-based) backup for fast recovery and a logical dump as a portable second copy.
What to back up
- All your databases, including views, stored routines, triggers and events.
- The binary logs since the last full backup (for point-in-time recovery).
- User accounts and grants. A dump of one database does not include them. Export with
SHOW CREATE USERandSHOW GRANTS, or take themysqlsystem schema carefully. - Configuration:
my.cnf, certificates, keyring files. Without the keyring or master key, encrypted data cannot be read. - Application files that belong to the data (uploads), because the database stores only the names.
Look at what binary logging is configured:
SELECT @@log_bin AS binary_logging, @@binlog_format AS format, @@binlog_expire_logs_seconds / 86400 AS keep_days;
+----------------+--------+-----------+
| binary_logging | format | keep_days |
+----------------+--------+-----------+
| 1 | ROW | 30.0000 |
+----------------+--------+-----------+
1 row in set
Binary logging is on by default in MySQL 8.x, in ROW format, and logs are kept for 30 days (binlog_expire_logs_seconds = 2592000). Check that your retention is longer than the time between full backups, or point-in-time recovery will have gaps. (On a managed service such as RDS or Cloud SQL, the provider configures this: check it.)
The 3-2-1 rule
- 3 copies of your data (the live one and two backups),
- on 2 different kinds of storage,
- with 1 copy off-site (another region or provider), preferably one that the server cannot delete (write-once or a separate account), because ransomware that reaches your server deletes the backups it can reach.
A backup account
The backup job needs read access, not write access, and never the root account (Application Users). The privileges that mysqldump really needed, tested on a MySQL 8.4 server, are:
CREATE USER 'backup'@'localhost' IDENTIFIED BY 'use-a-long-random-password';
GRANT SELECT, SHOW VIEW, TRIGGER, EVENT, SHOW_ROUTINE, RELOAD, PROCESS, REPLICATION CLIENT ON *.* TO 'backup'@'localhost';
What each is for, from the real error messages the server gave when a privilege was missing:
| Privilege | Needed for | Error without it |
|---|---|---|
SELECT, SHOW VIEW | Reading tables and views | Access denied on the table |
TRIGGER, EVENT | --triggers, --events | Access denied |
SHOW_ROUTINE (8.0.20 and later) | --routines | Access denied |
PROCESS | Dumping tablespace information | 'Access denied; you need (at least one of) the PROCESS privilege(s)' when trying to dump tablespaces (or use --no-tablespaces) |
RELOAD (or FLUSH_TABLES) | --source-data, --flush-logs | Couldn't execute 'FLUSH /!40101 LOCAL / TABLES': Access denied; you need (at least one of) the RELOAD or FLUSH_TABLES privilege(s) |
REPLICATION CLIENT | Reading the binary log position | Access denied |
LOCK TABLES | Only if you do not use --single-transaction | Access denied ... when using LOCK TABLES |
Keep the password out of the command line: use an option file with chmod 600 or mysql_config_editor (Password Security).
Schedule, retention, monitoring
| Item | A sensible starting point |
|---|---|
| Full backup | Daily (large systems: weekly full and daily incremental) |
| Binary logs | Kept at least as long as the oldest full backup you may restore |
| Retention | Daily for 7 to 14 days, weekly for 4 to 8 weeks, monthly for a year (or what regulation requires) |
| Where | Local disk for speed, plus an off-site copy |
| Encryption | Encrypt backup files, and store the key apart from the backups |
| Monitoring | The job reports success or failure. A backup that silently stopped last month is the usual disaster |
| Size check | Alert when a backup is much smaller than yesterday’s. A dump that stopped half way is smaller |
The restore drill
The only proof that a backup works is a restore:
- Pick a backup at random.
- Restore it on a different server.
- Check it: row counts, checksums, an application smoke test (Restoring and Verifying).
- Write down how long it took. That is your real RTO.
- Repeat on a schedule (every quarter), and after any change to the backup process.
Write the procedure as a runbook that someone who is not you can follow at 3 a.m.
Replication is not a backup
A replica copies every change, including the accidental DROP TABLE, within a second. It protects against hardware failure, not against mistakes or attacks. A delayed replica (one that runs, say, one hour behind) is a useful safety net, but it does not replace backups.