Logo
WP Fix by Blimx

Corrupted MySQL Tables — Recovery from innodb_force_recovery

Actualizado:
DatabaseRecovery

When MySQL won't start

You restart MySQL and the service refuses to come up. The error log shows:

InnoDB: Database page corruption on disk or a failed file read of page X
InnoDB: Cannot continue operation

WordPress is dead. Every request times out or shows "Error establishing database connection." This is the worst moment in WordPress recovery.

The good news: in most cases, the data is recoverable. The procedure is innodb_force_recovery, escalated level by level.

Stage 1 — Confirm corruption is the cause

systemctl status mysql
tail -100 /var/log/mysql/error.log

Look for InnoDB messages. Common patterns:

  • Database page corruption on disk — pages are damaged
  • Page lsn X but it should be Y — log sequence numbers don't match
  • Cannot find the appropriate table to look up — data dictionary inconsistency

These all indicate InnoDB corruption. Proceed with innodb_force_recovery.

The recovery levels

InnoDB has 6 escalating recovery levels (1-6). Each subsequent level is more invasive but enables MySQL to start in more broken states.

Edit /etc/mysql/my.cnf (or /etc/my.cnf depending on OS):

[mysqld]
innodb_force_recovery = 1

Restart MySQL:

systemctl start mysql

If MySQL starts, you can connect. If not, increase to 2, then 3, etc., up to 6.

What each level does

Level 1 (SRV_FORCE_IGNORE_CORRUPT): lets server run even if it detects corrupt page. Read-only operations possible.

Level 2 (SRV_FORCE_NO_BACKGROUND): prevents the master thread from running. Skip purging operations.

Level 3 (SRV_FORCE_NO_TRX_UNDO): doesn't run transaction rollback. Inconsistent state possible.

Level 4 (SRV_FORCE_NO_IBUF_MERGE): prevents insert buffer merge operations.

Level 5 (SRV_FORCE_NO_UNDO_LOG_SCAN): doesn't look at undo logs at all. Treats incomplete transactions as committed.

Level 6 (SRV_FORCE_NO_LOG_REDO): doesn't apply redo logs. Most destructive — data after last checkpoint is lost.

For WordPress recovery, you almost always want the LOWEST level that allows MySQL to start. Higher levels = more data loss.

The recovery procedure

Step 1 — Try level 1

innodb_force_recovery = 1

Restart. If MySQL starts, jump to Step 4.

Step 2 — Escalate if needed

If level 1 fails, try 2, then 3, then 4. After each:

systemctl start mysql

Stop at the first level that allows start.

Step 3 — At level 5 or 6, expect data loss

Levels 5 and 6 mean some recent data is lost. Accept this and proceed.

Step 4 — Verify what data is intact

mysql -u root -p
USE your_wp_database;
SELECT COUNT(*) FROM wp_posts;
SELECT COUNT(*) FROM wp_users;
SELECT MAX(post_date) FROM wp_posts WHERE post_status = 'publish';

If counts look reasonable and the latest post date is recent, you have most of your data.

Step 5 — Export ALL data immediately

mysqldump --single-transaction --quick --lock-tables=false \
  --no-tablespaces \
  your_wp_database > /backup/recovery-dump.sql

This is critical: you must export everything before doing anything else. The current MySQL state is fragile.

Step 6 — Stop MySQL, delete corrupted data files

systemctl stop mysql
cp -a /var/lib/mysql /var/lib/mysql.broken.$(date +%Y%m%d)
rm -rf /var/lib/mysql/your_wp_database

Step 7 — Remove innodb_force_recovery and restart

# innodb_force_recovery = 1   ← comment this out
systemctl start mysql

MySQL should start cleanly now (no more corruption since data dir is gone).

Step 8 — Recreate database, import dump

CREATE DATABASE your_wp_database;
mysql your_wp_database < /backup/recovery-dump.sql

Step 9 — Test WordPress

cd /var/www/yoursite
wp option get siteurl
wp post list --posts_per_page=5

If queries return data, WordPress is back.

Why this works

The corruption is in InnoDB's internal data structures, not your raw row data. innodb_force_recovery lets you bypass enough internal logic to read the rows. Dumping them gives you a clean SQL file. Importing into a fresh database recreates everything cleanly.

The data is the same; only the storage format is rebuilt.

What data might be lost

At levels 1-3: typically nothing. At level 4: insert buffer merges may not have happened — some recent inserts could be missing. At level 5: incomplete transactions are not rolled back — committed-but-not-flushed transactions may be lost. At level 6: changes since last checkpoint are lost — typically the last 30-60 minutes.

For WordPress, the data most likely to be lost is: - Last few orders (WooCommerce) - Comments posted in the last hour - Most recent log entries

Preventing future corruption

After recovery, configure MySQL for resilience:

[mysqld]
innodb_flush_log_at_trx_commit = 1
sync_binlog = 1
innodb_doublewrite = ON
innodb_file_per_table = 1
innodb_flush_method = O_DIRECT

Set up monitoring:

# Daily integrity check
0 3 * * * mysqlcheck --check --all-databases --silent > /var/log/mysqlcheck.log 2>&1

And REAL backups:

  • Binary backups: mariabackup or xtrabackup for fast point-in-time recovery
  • Off-site copies: rsync to a separate server or S3 nightly
  • Restore drills: monthly test that you can actually restore

Common mistakes during MySQL corruption recovery

  • Restarting MySQL repeatedly without changing config — doesn't help; corruption persists
  • Going straight to level 6 — loses more data than necessary; always start at 1
  • Not exporting before destroying data — once you delete /var/lib/mysql/<db>, that data is gone if dump failed
  • Removing innodb_force_recovery before exporting — MySQL goes back to refusing to start

When to call a specialist

InnoDB recovery is high-stakes. A wrong step at the wrong level can lose data permanently. We've recovered hundreds of corrupted WordPress databases without data loss.

Database recovery emergency within hours. For broader recovery see emergency support.