Mysql – Fixing MyISAM database without losing data

myisamMySQLmysql-5.5mysqldumptable

My old server was crashed but I was able to recover the databases from MySQL through testdrive.

I'm trying to dump a database using mysqldump but it gives error for certain tables are crashed and should repaired.

I tried myisamchk --safe-recover /var/lib/mysql/database/table.MYI
And also myisamchk -r -q /var/lib/mysql/database/table.MYI

The tables fix themselves but unfortunately they delete that data from MYD files.

Is there a way to fix the tables without losing data from MYD or is it possible to convert the MYD into human readable format like a txt file?

Best Answer

First of all, if your data is worth anything, don't store it in MyISAM.

I don't think you can reconstruct a table from corrupted MYD file. Records in MyISAM have different format that depends on values stored in each record. There are about 20 block types! To make it more fun the records in MyISAM can be fragmented. So, if you absolutely need the data from MyISAM table the only way I'm aware of is a C program with wildcard masks.

You can partially recover the table if you select from it ranges of the primary key. Corrupt PK ranges will be lost, but at least if the range is good you can get it back.

Here's a script that I wrote a while ago to read as many records is possible from a corrupted table. Try it out, I hope it'll help:

#!/bin/bash

# Adjust your values here
host=localhost
user=root
db=test
table=test
PK=id
newtable=newtest2
# end of user defined values

read -p "Password: " -s password
echo ""
if [ "xxx$password" = "xxx" ]
then
        pass_cmd=""
else
        pass_cmd="-p$password"
fi
mysql -h $host -u $user $pass_cmd -e "show databases" $db
if [ $? -eq 0 ]
then
        echo "Credentials are ok!"
else
        echo "Can't run query SHOW DATABASES"
        echo "Check what's wrong"
        exit
fi
minPK=`mysql -sN -h $host -u $user $pass_cmd -e "SELECT MIN($PK) FROM $table" $db`
maxPK=`mysql -sN -h $host -u $user $pass_cmd -e "SELECT MAX($PK) FROM $table" $db`
a=$minPK
b=$maxPK
let chunk=$b-$a

function insert_range {
q="INSERT IGNORE INTO \`$newtable\` SELECT * FROM \`$table\` WHERE \`$PK\` >= $1 and \`$PK\` <= $2"
mysql -h $host -u $user $pass_cmd -e "$q" $db 2>/dev/null
}
successful_tries=0
echo "Primary key ($PK) range: $minPK .. $maxPK"
while true
do
        insert_range $a $b
        if [ $? -eq 0 ]
        then
                echo "Good, primary key range: $a .. $b"
                let a=$b+1
                let b=$b+$chunk
                let successful_tries=$successful_tries+1
        else
                echo "Bad luck, primary key range: $a .. $b"
                let chunk=$chunk/2
                let b=$a+$chunk
                mysqladmin -h $host -u $user $pass_cmd ping > /dev/null 2>&1
                while [ $? -ne 0 ]
                do
                        mysqladmin -h $host -u $user $pass_cmd ping > /dev/null 2>&1
                done
                successful_tries=0
        fi
        if [ $a -gt $maxPK ]
        then
                exit
        fi
        if [ $chunk -eq 0 ]
        then
                let a=$a+1
                let b=$a+1
                let chunk=1
        fi
        # If things are going well, increase pace
        if [ $successful_tries -gt 5 ]
        then
                let chunk=$chunk*2
        fi
done