Ah, the wonderful feeling of being hauled out of the spa/candlelit
dinner/Quake game to be told in panicked tones that there is a
'database
error'. You rattle off the usual suspects, determining that their
keyboard is
actually plugged in, monitor on, monitor's brightness turned up (yes,
it has
happened to me), and that they are actually on the right page.
Eventually the
conclusion is inescapable - there is no other alternative, the database
really
is behaving strangely. Some cynics may say it happens more than usual with MySQL, and
looking
through the list of MySQL bugfixes is enough to strike terror into the
hardiest
of souls. From the MySQL documentation: INSERT DELAYED ... SELECT ... could cause table corruption
because tables were not locked properly. This is now fixed by ignoring
DELAYED
in this context. (Bug #1983) Fixed bug in overrun check for BLOB values with compressed
tables. This was a bug introduced in 4.0.14. It caused MySQL to regard
some
correct tables containing BLOB values as corrupted. (Bug #770, Bug
#1304, and
maybe Bug #1295) Fixed rare bug in
MYISAM introduced in 4.0.3 where the index file header was not updated
directly
after an UPDATE of split dynamic rows. The symptom was that the table
had a
corrupted delete-link if mysqld was shut down or the table was checked
directly
after the update. Comparison/sorting for latin1_de character set was rewritten.
The
old algorithm could not handle cases like "sä" < "ßa".
See section 5.6.1.1 German character set. In rare cases, it resulted in
table
corruption. But then, has anyone seen a SQL-Server buglist recently?
Nevertheless, table
corruption should be rare when using MySQL (though an overheating
server
continually restarting at the most inopportune times has meant I have
seen more
than my fair share of corruption recently, hence the inspiration for
the
article). Luckily, MySQL has some easy-to-use tools that can easily
repair most
cases of table corruption, and this article introduces you to these.
You should
always look at removing the causes of the corruption of course, but
this
article only deals with the firefighting aspect - repairing the
symptoms. Table corruption should be relatively easy to identify. Queries that
worked
before suddenly stop working, or begin working inconsistently. Your
first
suspected culprit would be the code of course, but when a query such as
UPDATE
table_x SET x_key='d' doesn't work for no good reason, it's time
to check
the tables. If you see any of the following errors, it is also prudent
to check
the tables for corruption: The latter error returns an error number, and you can get more
details about
this error with the perror utility. perror sits in
the same
directory as all the other MySQL binaries, such as mysqladmin,
mysql
and those we'll discuss shortly, such as mysqlcheck and myisamchk.
Some of the errors, which often indicate table corruption, include: There are three ways to check tables. All of these work with MyISAM
tables,
the default, non-transactional table type, and one with InnoDB, the
most mature
of the MySQL transactional table types. Fortunately, MySQL now allows
you to
check tables while the server is still running, so corruption in a
minor table
need not affect everything on the server. The first method for checking tables is to run the CHECK TABLE
SQL
statement while connected to the server. The syntax is:
CHECK TABLE tablename[,tablename2...] [option][,option2...],
for
example: There are a number of options to specify as well, which allow you to
do a
more in-depth, or a more superficial kind of check than normal: QUICK The quickest option, and does not scan the rows to check for
incorrect links. Often used when you do not suspect an error. FAST Only checks tables if they have not been closed properly.
Often used when you do not suspect an error, from a cron, or after a
power failure that seems to have had no ill-effects. CHANGED Same as FAST, but also checks tables that have been changed
since the last check. MEDIUM The default if no option is supplied. Scans rows to check that
deleted links are correct, and verifies a calculated checksum for all
keys with a calculated a key checksum for the rows. EXTENDED The slowest option, only used if the other checks report no
errors but you still suspect corruption. Very slow, as it does a full
key lookup for all keys for every row. Increasing the key-buffer-size
variable in the MySQL config. file can help this go quicker. Note that CHECK TABLE only works with MyISAM and InnoDB
tables. If
CHECK finds corruption, it will mark the table as corrupt, and it will
be
unusable. See the Repairing tables section below for how to
handle this.
The second method is to run the mysqlcheck command-line
utility. The
syntax is: The following options pertain to checking (mysqlcheck can also
repair, as
well as analyze and optimize, which are not covered here). --auto-repair Used together with a check option, it will automatically begin
repairing if corruption is found. --check, -c Checks tables (only needed if using mysqlcheck under another
name, such as mysqlrepair. See the manual for more details) --check-only-changed, -C Same as the CHECK TABLE ... CHANGED option above. --extended, -e Same as the CHECK TABLE ... EXTENDED option above. --fast, -F Same as the CHECK TABLE ... FAST option above. --medium-check, -m Same as the CHECK TABLE ... MEDIUM option above. --quick, -q Same as the CHECK TABLE ... QUICK option above. For example: Note that you can specify multiple tables, and that mysqlcheck
only works with MyISAM tables. Finally, there is the myisamchk command-line utility. The server
must be
down, or the tables inactive (which is ensured if the --skip-external-locking
option is not in use). The syntax is: myisamchk
[options] tablename.MYI,
and you must be in, or specify, the path to the relevant .MYI files
(each
MyISAM database is stored in its own directory). These are the
available check
options: --check, -c The default option --check-only-changed, -C Same as the CHECK TABLE ... CHANGED option above. --extend-check, -e Same as the CHECK TABLE ... EXTENDED option above. --fast, -F Same as the CHECK TABLE ... FAST option above. --force, -f Will run the myisamchk repair option if any errors are found --information, -i Display statistics about the checked table --medium-check, -m Same as the CHECK TABLE ... MEDIUM option above. --read-only, -T Does not mark the table as checked --update-state, -U This option stores when the table was checked, and the time of
crash, in .MYI file. For example: You can also use wildcard to check all the .MYI tables at
the same time, for example: Note that myisamchk only works with MyISAM tables.
For those of you still using the old ISAM table types, there is also isamchk,
though there is really little reason not to upgrade to MyISAM.Fixed in 4.0.18
Fixed in 4.0.16
Fixed in 4.0.15
Fixed in 4.0.14
Identifying table corruption
Checking tables
Checking tables with CHECK TABLE
mysql> CHECK TABLE fixtures;
+-------------------------+-------+----------+----------+
| Table | Op | Msg_type | Msg_text |
+-------------------------+-------+----------+----------+
| sports_results.fixtures | check | status | OK |
+-------------------------+-------+----------+----------+
1 row in set (0.01 sec)
Checking tables with mysqlcheck
mysqlcheck [options] dbname tablename [tablename2... ].
% mysqlcheck -uuser -ppass sports_results fixtures
sports_results.fixtures OK
Checking tables with myisamchk
% myisamchk fixtures.MYI
Checking MyISAM file: fixtures.MYI
Data records: 1415 Deleted blocks: 2
- check file-size
- check key delete-chain
- check record delete-chain
- check index reference
- check data record references index: 1
- check record links
% myisamchk *.MYI
In most cases, only the index will be corrupted (the index is a separate, smaller, file with records that point to the main data file) - actual data corruption is extremely rare. Fixing most forms of corruption is relatively easy. As with checking, there are three ways to repair tables. These all only work with MyISAM tables - to repair corruption of the other table types, you will need to restore from backup:
Repairing a table requires twice as much disk space as the original table (a copy of the data is made), so make sure you are not going to run out of disk space before you start.
The syntax is, as would be expected, REPAIR TABLE tablename[,tablename1...] [options]. This method only works with MyISAM tables. The following options are available.
|
QUICK |
The quickest, as the data file is not modified. |
|
EXTENDED |
Will attempt to recover every possible data row file, which can result in garbage rows. Use as a last resort. |
|
USE_FRM |
To be used if the .MYI file is missing or has a corrupted header. Uses the .frm file definitions to rebuild the indexes. |
In most cases, a simple REPAIR without any options should work fine. An unusual case is when the .MYI is missing. Here is what would happen:
mysql> REPAIR TABLE fixtures;
+-------------------------+--------+----------+---------------------------------------------+
| Table | Op | Msg_type | Msg_text |
+-------------------------+--------+----------+---------------------------------------------+
| sports_results.fixtures | repair | error | Can't find file: 'fixtures.MYI' (errno: 2) |
+-------------------------+--------+----------+---------------------------------------------+
The repair has failed because the index file is missing or has a corrupted header. To use the definition file to repair, use the USE_FRM option, as follows:
mysql> REPAIR TABLE fixtures USE_FRM;
+-------------------------+--------+----------+------------------------------------+
| Table | Op | Msg_type | Msg_text |
+-------------------------+--------+----------+------------------------------------+
| sports_results.fixtures | repair | warning | Number of rows changed from 0 to 2 |
| sports_results.fixtures | repair | status | OK |
+-------------------------+--------+----------+------------------------------------+
Everything has gone smoothly this time, as indicated by the OK Msg_text.
The mysqlcheck command-line utility can be used while the server is running, and, like all the methods of repair, only works with MyISAM tables. The syntax is:
%mysqlcheck -r sports_results fixtures -uuser -ppass
sports_results.fixtures OK
You can also repair multiple tables in a database, by listing them after the database name, or all tables in a database by just passing the database name, for example:
%mysqlcheck -r sports_results fixtures events -uuser -ppass
sports_results.fixtures OK
sports_results.events OK
%mysqlcheck -r sports_results -uuser -ppass
sports_results.fixtures OK
sports_results.events OK
...
The server must be down, or the tables inactive (which is ensured if the --skip-external-locking option is not in use). The syntax is myisamchk [options[ [tablenames]. Remember again that you must be in, or specify, the path to the relevant .MYI files. The following options are available:
|
--backup, -B |
Makes a .BAK backup of the table before repairing it |
|
--correct-checksum |
Corrects the checksum |
|
--data-file-length=#, -D # |
Specifies the maximum length of the data file, when recreating |
|
--extend-check, -e |
Attempts to recover every possible row from the data file. This option should not be used except as a last resort, as it may produce garbage rows. |
|
--force, -f |
Overwrites old temporary .TMD files instead of aborting if it encounters a pre-existing one. |
|
keys-used=#, -k # |
Can make the process faster by specifying which keys to use. Each binary bit stands for one key starting at 0 for the first key. |
|
--recover, -r |
The most commonly used option, which repairs most corruption. If you have enough memory, increase the sort_buffer_size to make the recover go more quickly. Will not recover from the rare form of corruption where a unique key is not unique. |
|
--safe-recover, -o |
More thorough, yet slower repair option than -r, usually only used only if -r fails. Reads through all rows and rebuilds the indexes based on the rows. This also uses slightly less disk space than a -r repair since a sort buffer is not created. You should increase the key_buffer_size value to improve repair speed if there is available memory. |
|
--sort-recover, -n |
MySQL uses sorting to resolve the indexes, even if the resulting temporary files are very large. |
|
--character-sets-dir=... |
The directory containing the character sets |
|
--set-character-set=name |
Specifies a new character set for the index |
|
--tmpdir=path, -t |
Passes a new path for storing temporary files if you dont want to use the contents of the TMPDIR environment variable |
|
--quick, -q |
The fastest repair, since the data file is not modified. A second -q will modify the data file if there are duplicate keys. Also uses much less disk space since the data file is not modified. |
|
--unpack, -u |
Unpacks a file that has been packed with the myisampack utility. |
An example of its usage:
% myisamchk -r fixtures
- recovering (with keycache) MyISAM-table 'fixtures.MYI'
Data records: 0
I hope that you will never have to do any repairing, but I am sure at least some of you reading this article (at least those who got this far) are here precisely because you've got some corruption. I hope that your repairs will be as painless as all mine have been. Good luck!