How to repair a MySQL server which is showing corrupted tables. How to repair InnoDB corruption.

Today one of our hosting servers crashed and when we managed to get it up and running, MySQL wouldn’t start.

I started reading to see what could have happened and in the log I came across this:

“The manual page at http://dev.mysql.com/doc/mysql/en/crashing.html contains information that should help you find out what is causing the crash.” So there I went and I after reading through the whole page, I came out with nothing clear!

So I went back to the log and saw this:

 InnoDB: Database was not shut down normally!
 InnoDB: Starting crash recovery.
 InnoDB: Reading tablespace information from the .ibd files...
 InnoDB: Restoring possible half-written data pages from the doublewrite
 InnoDB: buffer...
 InnoDB: Doing recovery: scanned up to log sequence number 135274039561
 InnoDB: Database page corruption on disk or a failed
 InnoDB: file read of page 54838.
 InnoDB: You may have to recover from a backup.

I then decided I had to recover MySQL. Being a thing I had never done, I started reading as much as I could and I found this very interesting page: http://blackbird.si/mysql-corrupted-innodb-tables-recovery-step-by-step-guide/ which actually got to the problem of solving corrupt databases but not the problem I had: MySQL as such was corrupt!

These are the steps I took:

1. I stopped the MySQL service with the following command (BTW, I was running  CentOS 6):

service mysqld stop

 

2. I edited the file with the MySQL configuration:

nano /etc/my.cnf

(vi /etc/my.cnf will also work, of course)

 

3. I added the following line:

innodb_force_recovery=1

Why? Here are the main options for this parameter: http://dev.mysql.com/doc/refman/5.0/en/forcing-innodb-recovery.html

 

4. I started MySQL

service mysqld start

so MySQL started and after a few seconds it crashed again!

 

5. I then changed the innodb_force_recovery value to 2 with no result. Then I tried 3 and the same: crash! So in the end I left this value:

innodb_force_recovery=4

This is the maximum level of innodb_force_recovery at which no data is lost. Levels 5 and 6 could lead to a loss of data.

At this point MySQL was up and the databases running but no changes coud be done to any database. (Remember that any innodb_force_recovery level different from 0 puts the databases in “read only” mode).

In the mysqld.log I had thousands of lines like these:

 InnoDB: A new raw disk partition was initialized or
 InnoDB: innodb_force_recovery is on: we do not allow
 InnoDB: database modifications by the user. Shut down
 InnoDB: mysqld and edit my.cnf so that newraw is replaced
 InnoDB: with raw, and innodb_force_... is removed.

At this stage it was clear MySQL was clearly corrupt and I had to recover it.

This was the plan:

– Backup all the databases.
– Reinstall MySQL
– Recover all the databases.

 

6. I started by backing up all the databases I had. Just to be safe, I did it in two different ways: a full backup with all the databases in one file and a full backup with every database in its own file.

I wanted all the backups to be in one place, so I created a special folder for this purpose:

mkdir /databasebackups

To have all the databases in one file I ran this command:

mysqldump -u root -p --all-databases > /databasebackups/alldb.sql

(If you don’t have the root MySQL password, you can change it via any of these methods: https://dev.mysql.com/doc/refman/5.5/en/resetting-permissions.html)

And just in case, I ran this little script (written by JRuzafa – http://stackoverflow.com/users/944571/jruzafa) to have every database in its own file:

#!/bin/bash
USER="root"
 PASSWORD="here-goes-the-root-mysql-password"
 #OUTPUT="/databasebackups/independentdatabases"
#rm "$OUTPUTDIR/*gz" > /dev/null 2>&1
databases=`mysql -u $USER -p$PASSWORD -e "SHOW DATABASES;" | tr -d "| " | grep -v Database`
for db in $databases; do
 if [[ "$db" != "information_schema" ]] && [[ "$db" != "performance_schema" ]] && [[ "$db" != "mysql" ]] && [[ "$db" != _* ]] ; then
 echo "Dumping database: $db"
 mysqldump -u $USER -p$PASSWORD --databases $db > $OUTPUTDIR/`date +%Y%m%d`.$db.sql
 # gzip $OUTPUT/`date +%Y%m%d`.$db.sql
 fi
 done

If you don’t know how to save it, just create a file called dbbackup.sh and copy and paste the content. Then run sh dbbackup.sh

 

7. With the databases backed up, I now had to reinstall/recover MySQL. The first thing was to stop the MySQL service:

service mysqld stop

 

8. I wasn’t 100% sure if I was going to mess everything up, so just in case I also backed up the actual MySQL files:

ibdata1
ib_logfile0
ib_logfile1

with the following command:

cp /var/lib/mysql/ib* /databasebackups/

 

9. I then deleted all the MySQL databases. I went to the database location:

cd /var/lib/mysql/

and deleted all the database folders and their contents:

 rm -rf here-goes-the-name-of-your-database-1
 rm -rf here-goes-the-name-of-your-database-2
 rm -rf here-goes-the-name-of-your-database-3
 rm -rf here-goes-the-name-of-your-database-4

but DO NOT DELETE the mysql folder!

Once you’ve deleted all the databases, in your database folder you should have the following content:

ibdata1
ib_logfile0
ib_logfile1
mysql/
mysql.sock

The last step is to delete the innodb file and logs:

 rm ibdata1
 rm ib_logfile0
 rm ib_logfile1

after this the content of the database folder should be:

mysql/
mysql.sock

 

10. At this stage all I had to do was start MySQL and import all the databases. To start MySQL I ran the following command:

service mysqld start

And I checked in the log that it had started correctly:

 [Note] /usr/libexec/mysqld (mysqld 5.5.47) starting as process 11927 ...
 [Note] Plugin 'FEDERATED' is disabled.
 InnoDB: The InnoDB memory heap is disabled
 InnoDB: Mutexes and rw_locks use GCC atomic builtins
 InnoDB: Compressed tables use zlib 1.2.3
 InnoDB: Using Linux native AIO
 InnoDB: Initializing buffer pool, size = 128.0M
 InnoDB: Completed initialization of buffer pool
 InnoDB: The first specified data file ./ibdata1 did not exist:
 InnoDB: a new database to be created!
 InnoDB: Setting file ./ibdata1 size to 10 MB
 InnoDB: Database physically writes the file full: wait...
 InnoDB: Log file ./ib_logfile0 did not exist: new to be created
 InnoDB: Setting log file ./ib_logfile0 size to 5 MB
 InnoDB: Database physically writes the file full: wait...
 InnoDB: Log file ./ib_logfile1 did not exist: new to be created
 InnoDB: Setting log file ./ib_logfile1 size to 5 MB
 InnoDB: Database physically writes the file full: wait...
 InnoDB: Doublewrite buffer not found: creating new
 InnoDB: Doublewrite buffer created
 InnoDB: 127 rollback segment(s) active.
 InnoDB: Creating foreign key constraint system tables
 InnoDB: Foreign key constraint system tables created
 InnoDB: Waiting for the background threads to start
 InnoDB: 5.5.47 started; log sequence number 0

and 3 new files were created in the database folder (remember, /var/lib/mysql):

ibdata1
ib_logfile0
ib_logfile1

 

11. To import all the databases there are 2 ways:

a) To import the alldb.sql file that we created in step 6. Here is the command to do it:

mysql -u root -p < /databasebackups/alldb.sql

No progress is shown, so you’ll just have to wait until the command finishes its execution.

b) To import all the independent databases with each one having it’s own .sql file (stored in the /databasebackups/independentdatabases folder)

The long way would be:

 mysql -u root -p < /databasebackups/here-goes-the-name-of-your-database-1
 mysql -u root -p < /databasebackups/here-goes-the-name-of-your-database-2
 mysql -u root -p < /databasebackups/here-goes-the-name-of-your-database-3
 mysql -u root -p < /databasebackups/here-goes-the-name-of-your-database-4

And the short way would be using a script written by Corey (http://stackoverflow.com/users/290699/corey) which can be found here:  https://gist.github.com/tenold/aa5e107d93c0f54436cb

#!/bin/bash
USER="root"
 PASSWORD="here-goes-your-root-mysql-password"
FILES="/databasebackups/independentdatabases/*"
for f in $FILES
 do
 echo "Processing $f file..."
 mysql -u $USER -p$PASSWORD < $f
 done

 

And after all this process, your MySQL server and databases should be up and running!

 

Please leave comments if you wish to!

 

Daniel

Leave a Reply

Your email address will not be published. Required fields are marked *