MySQL — Восстановление таблиц Innodb

Шеф! Все пропало! База упала, данные пропали!

mysql> USE helldb;
mysql> SELECT * FROM cheaters;
ERROR 1146 (42S02): TABLE 'helldb.cheaters' doesn't exist
mysql>

Или это может выглядеть вот так, при попытке забекапить базу mysqldump -ом:

[red@hellsrv ~]$ mysqldump -uroot -p helldb > helldb.sql
Enter password:
mysqldump: Got error: 1146: Table 'helldb.cheaters' doesn't exist when using LOCK TABLES
[red@hellsrv ~]$

Перед любыми действиями с базой, обязательно сделайте ее бэкап!

Поскольку mysqldump в нашем случае не вариант, то на уровне файловой системы самое то. Для этого остановить сервер mysql:

[red@hellsrv ~]$ sudo service mysql stop

И скопируйте сбойную базу в директорию где вы храните бэкапы:

[red@hellsrv ~]$ sudo cp -R /var/lib/mysql/helldb /some/dir/to/backup

Уже после бэкапа, беремся за восстановление всеми силами.
Судя по файликам, таблицы в InnoDB, для того чтобы восстановить эти таблицы нам нужны 2 вещи:
— узнать структуру таблиц
— иметь файлики с данными (имеется ввиду файлы на уровне файловой системы)

Особенность Innodb такова, что таблица на уровне файловой системы состоит из двух фалов:

  • файла .frm — он хранит в себе структуру таблицы;
  • файла .ibd — это собственно данные;
  • Идея восстановления следующая:
    1. узнать структуру «битой» таблицы
    2. создать чистую базу
    3. создать в ней таблицу нужной структуры (читай п.1)
    4. скопировать данные в новую таблицу из старой
    5. если данные окажутся битыми, можно попробовать восстановить их используя утилиту innochecksum

    У меня есть новый сервер debnew с установленным Debian 8, в его официальных репах есть пакет с нужной нам утилитой для чтения структуры таблиц InnoDB из файлика frm.
    Ставим нужный пакетик:

    root@debnew:~# apt-get install mysql-utilities

    Используется она вот так:

    $ mysqlfrm --diagnostic some_table.frm

    Еще нюанс: на новом сервере я установил mariadb-server, запустил его и создал отдельную чистую базу. Туда по одной создавал таблицы и подливал данные с битых таблиц со старого сервера.
    Для создания базы на новом сервере мне нужно было узнать какая кодировка была в старой базе. Это можно посмотреть на сервере с битой базой:

    mysql> SELECT DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = 'helldb';
    +----------------------------+------------------------+
    | DEFAULT_CHARACTER_SET_NAME | DEFAULT_COLLATION_NAME |
    +----------------------------+------------------------+
    | cp1251                     | cp1251_general_ci      |
    +----------------------------+------------------------+
    1 ROW IN SET (0.00 sec)

    mysql>

    Создаем новую базу в mariadb:

    MariaDB [(NONE)]> CREATE DATABASE helldb CHARACTER SET cp1251 DEFAULT COLLATE cp1251_general_ci;
    Query OK, 1 ROW affected (0.00 sec)

    MariaDB [(NONE)]>

    Ну и дальше поехали таблички восстанавливать … одна за другой. Начну с простой таблички, чтоб много не писать.
    Узнаем структуру:

    [red@debnew mysql_recovery]$ mysqlfrm --diagnostic articles.frm
    # WARNING: Cannot generate character set or collation names without the --server option.
    # CAUTION: The diagnostic mode is a best-effort parse of the .frm file. As such, it may not identify all of the components of the table correctly. This is especially true for damaged files. It will also not read the default values for the columns and the resulting statement may not be syntactically correct.
    # Reading .frm file for articles.frm:
    # The .frm file is a TABLE.
    # CREATE TABLE Statement:

    CREATE TABLE `articles` (
      `id` int(10) unsigned NOT NULL comment 'Article ID',
      `name` varchar(128) NOT NULL comment 'Article name',
    PRIMARY KEY `PRIMARY` (`id`)
    ) ENGINE=InnoDB COMMENT 'Catalogue of articles';

    #...done.
    [red@debnew mysql_recovery]$

    Берем командочку из предыдущего шага и создаем в чистой базе такую таблицу:

    MariaDB [(NONE)]> USE helldb;
    DATABASE changed
    MariaDB [helldb]> CREATE TABLE `articles` (
        ->   `id` INT(10) UNSIGNED NOT NULL comment 'Article ID',
        ->   `name` VARCHAR(128) NOT NULL comment 'Article name',
        -> PRIMARY KEY `PRIMARY` (`id`)
        -> ) ENGINE=InnoDB COMMENT 'Catalogue of articles';
    Query OK, 0 ROWS affected (0.30 sec)

    MariaDB [helldb]>

    Таблица с нужной структурой у нас есть, теперь нужно скопировать в нее данные. Они хранятся в файлике `table_name`.ibd, такой файлик автоматом создается во время создания таблицы (даже если она без данных). Вот он на новом сервере:

    debnew:/home/mysql/helldb# ls -l
    total 104
    -rw-rw---- 1 mysql mysql  1094 Apr  9 17:07 articles.frm
    -rw-rw---- 1 mysql mysql 98304 Apr  9 17:07 articles.ibd
    -rw-rw---- 1 mysql mysql    65 Apr  9 17:05 db.opt
    debnew:/home/mysql/helldb#

    сначала нужно очистить тот файлик что у нас есть, потом подлить (на уровне FS) со старой базы, и импортнуть. Для этого в mysql консоли чистим новый пустой файл данных:

    MariaDB [helldb]> ALTER TABLE articles DISCARD TABLESPACE;
    Query OK, 0 ROWS affected (0.04 sec)

    MariaDB [helldb]>

    файлик удалился, убедимся:

    debnew:/home/mysql/helldb# ls -l
    total 8
    -rw-rw---- 1 mysql mysql 1094 Apr  9 17:07 articles.frm
    -rw-rw---- 1 mysql mysql   65 Apr  9 17:05 db.opt
    debnew:/home/mysql/helldb#

    копируем имеющийся articles.idb с мертвой базы (можно из бекапа):

    debnew:/home/mysql/helldb# cp /home/work/mysql_recovery/helldb.bkp/articles.ibd articles.ibd
    debnew:/home/mysql/helldb# chown mysql.mysql articles.ibd

    пробуем импортировать данные, в mysql консоли:

    MariaDB [helldb]> ALTER TABLE articles IMPORT TABLESPACE;
    Query OK, 0 ROWS affected, 1 warning (0.50 sec)

    MariaDB [helldb]>

    Ругани нету, проверяем есть ли что в таблице теперь:

    MariaDB [helldb]> SELECT * FROM articles LIMIT 10;
    +-----+-----------+
    | id  | name      |
    +-----+-----------+
    | 333 | Gigabytes |
    | 777 | Megabytes |
    +-----+-----------+
    2 ROWS IN SET (0.00 sec)

    MariaDB [helldb]>

    Отлично! Если есть еще сбойные таблицы, повторяем процедуру для других таблиц 🙂 После этого делаете mysqldump новой базы и заливаете его уже куда хотите 🙂

    Хай щастить!

    1. Комментов пока нет

    1. Трэкбэков пока нет.

    Why ask?