Symptom
systemctl start mysqld fails, and the error log (/var/log/mysqld.log or SHOW VARIABLES LIKE 'log_error' when it was last running) shows lines such as:
[ERROR] [MY-011906] [InnoDB] Database page corruption on disk or a failed file read of page [page id: space=45, page number=3]
[ERROR] [MY-013183] [InnoDB] Assertion failure: ...
InnoDB: We intentionally generate a memory trap.
Typical triggers: power loss with write caching on, storage failure, disk full during writes, a killed process during an upgrade.
Step 0: decide restore vs repair
| You have | Usually best |
|---|---|
| Recent backup + binlogs (XtraBackup, MySQL Enterprise Backup, snapshots) | Restore and replay binlogs to just before the crash |
| A healthy replica | Promote it; rebuild this server from it |
| Neither | Forced recovery and dump (this runbook) |
Step 1: copy the data directory
systemctl stop mysqld
cp -a /var/lib/mysql /backup/mysql-datadir-$(date +%F-%H%M)
Every step below can make things worse. Keep an untouched copy.
Step 2: check the basics
- Disk space:
df -h /var/lib/mysql(InnoDB fails strangely on a full disk). - Permissions: files owned by
mysql. - Memory: an
innodb_buffer_pool_sizelarger than RAM after a VM resize causes failures that look like crashes. - Did an upgrade or downgrade happen? MySQL can't start an 8.4 data directory with 8.0 binaries.
If one of these is the cause, fix it and start normally. InnoDB crash recovery runs automatically.
Step 3: raise innodb_force_recovery step by step
Add to my.cnf under [mysqld], start, and check whether it stays up:
[mysqld]
innodb_force_recovery = 1
| Level | Effect (cumulative) | Risk |
|---|---|---|
| 1 | Ignore corrupt pages | Low |
| 2 | Don't run the purge thread | Low |
| 3 | Don't roll back incomplete transactions | Medium |
| 4 | Don't calculate statistics / skip change buffer merges | Can permanently corrupt data files |
| 5 | Don't look at undo logs | High |
| 6 | Don't do redo log roll-forward | High |
Start at 1 and go up only if it still won't start. With levels ≥ 4, treat the data files as disposable: you're only reading data out.
The server runs read-only for user data at level 4 and above (and you shouldn't write at any level).
Step 4: dump the data
mysqldump --all-databases --routines --events --triggers --single-transaction \
--set-gtid-purged=OFF > /backup/full-dump.sql
If one table makes the dump crash, dump database by database, then table by table, to find it:
for t in $(mysql -N -e "SELECT table_name FROM information_schema.tables WHERE table_schema='app'"); do
mysqldump app "$t" > /backup/app.$t.sql || echo "FAILED: $t"
done
For a broken table, try SELECT ... INTO OUTFILE with a WHERE on primary key ranges to save the readable rows.
Step 5: rebuild
- Stop MySQL, move the old data directory aside (keep it).
- Initialise a fresh one:
mysqld --initialize --user=mysql(note the temporary root password in the log). - Remove
innodb_force_recoveryfrommy.cnf; start. - Load the dump:
mysql < /backup/full-dump.sql. - Check row counts against what the application expects; run
CHECK TABLEon key tables. - Recreate users if you didn't dump the
mysqlsystem schema (8.0+: usemysqlpump/ MySQL Shell dump utilities, or scriptSHOW CREATE USER/SHOW GRANTS).
Caution
Never leave
innodb_force_recoveryset in production config. Remove it as soon as the data is dumped. A server running with level 4+ can silently damage data further.
Prevent it next time
- Storage with battery/flash-backed write cache or no volatile write caching;
innodb_flush_log_at_trx_commit = 1andsync_binlog = 1for durable workloads. - Regular physical backups with restore tests; binlogs kept for point-in-time recovery.
- A replica (or InnoDB Cluster) so recovery is a failover, not a forensic exercise.
- Disk-space alerts well before full.