Pull to refresh

История восстановления базы MySQL из файлов (InnoDB)

Reading time 10 min
Views 57K
Как говорит народная мудрость, “админы делятся на две категории: те, которые делают бэкапы, и те, которые уже делают”. В моем случае ответственность за несделанный бэкап упала на разработчика, то есть на меня самого. Данная статья посвящена тому, как найти выход из ситуации, подобной описанной. Надеюсь она будет полезна тем, кто не имея такого опыта, может столкнуться с подобной ситуацией.

В общем, дело было так…

Вступление


В далеком царстве, в тридевятом государстве В одном из государственных административных учреждений, которое обслуживается нашим предприятием, стоял сервер. В качестве сервера использовался обычный настольный компьютер с не особо мощным двухядерным процессором, жестким диском на 320 Гб, без всяких там “ненужных” RAID-массивов, ничего “лишнего”, c установленной на нем cерверной Windows, кажется, 2003. И выполнял он некоторые серверные задачи, о которых я-то в силу своей должности не был особо осведомлен. Работал так практически без перерыва порядка нескольких лет.

И вот, в один прекрасный день, позвал меня с моей напарницей к себе начальник отдела, дал нам задание разработать систему для этого учреждения с использованием в качестве СУБД MySQL Server 5.1. Систему мы разработали, все ей были весьма довольны, и пришла пора ее внедрять. Без всяких проблем мы с одним из наших админов, придя в офис учреждения, установили на вышеупомянутый сервер MySQL 5.1, развернули на ней базу, установили клиентское приложение на рабочие машины, запустили процесс внедрения. Работники учреждения были весьма рады системе, но осваивали ее медленно ввиду загруженности работой, совещаниями, заседаниями, обращениями граждан и т.д. Потихоньку вводили информацию, но сильно “растягивали удовольствие”. Надо было уже потихоньку начинать задумываться о стратегии бэкапа/восстановления, но я все думал о том, что это и не совсем-то мое дело, ведь я-то не админ, а разработчик. Но и админам это было совершенно безразлично. Я же себя утешал лишь тем, что пока информации мало, вот будет побольше, и если никто не начнет делать бэкапы, начну уже я. Оглядываясь назад, теперь я вижу, как легко можно было предсказать последующие события.

Через некоторое время одного из ключевых сотрудников настигло “просветление”, и за очень малый промежуток времени было введено значительное количество информации в систему, практически вся информация, которая могла быть введена на тот момент. Я об этом и думать не думал, все полагал, что они также медленно присматриваются к системе, ничего толком еще не вводя, работая себе, как и раньше, только со своими бумагами. Сотрудник же ввел быстренько информацию и ушел в отпуск на месяц.

Основные боевые действия


И вот, наступил другой “прекрасный” день, сервер упал. Причем, не программно, а аппаратно – “полетел” жесткий диск. Далее жесткий диск был сдан в фирму, занимающуюся восстановлением данных, и значительная часть информации с него была восстановлена. Однако, админы при восстановлении самого сервера, восстановили только всю информацию с диска D, про нашу MySQL никто и не вспомнил (по “счастливой случайности” она оказалась поставлена на диск C). Все это время я себе спокойно занимался другими проектами, про эти события и знать не знал.

Прошло еще некоторое время, и вышеупомянутый сотрудник учреждения выходит из отпуска. Звонит мне и говорит о том, что не может войти в систему. Я, как обычно, спрашиваю, какое сообщение при этом выдает система, и все такое, сходу понимаю, что что-то не так с подключением к базе. Далее узнаю уже от нее эту историю с падением сервера, с тем, что там “что-то меняли”, и понимание мое становится еще более глубоким: теперь я понял, что нашей базы данных вообще нет на сервере. Причем, как выяснилось, нет ни базы, ни самой СУБД. Расспрашиваю админов, что же там все-таки было, что восстановили, что нет… прихожу к неутешительному выводу, что базы нет, и большой вопрос, удастся ли ее восстановить. Беру себе копию восстановленной с убитого жесткого диска информации и начинаю в ней копаться.

Первым делом ищу папку Program Files, куда ставится по умолчанию MySQL. Не нахожу, но зато нахожу папку с файлами данных MySQL, в ней папка с именем той самой нашей базы. Небольшой заряд оптимизма немного разогнал тучи моего отчаяния, но… глядя на файлы *.MYD, в которых по идее должны бы быть записи таблиц, вижу, что размер их слишком мал для такого количества информации, которое было введено. Бинарные логи также не были включены, и этот вариант автоматически отпадал. Начинаю думать над этим вопросом и вспоминаю, что можно взглянуть на девелоперскую базу на нашем тестовом сервере, что я и сделал. Там я увидел, что большинство таблиц – причем, самых важных – работают на движке InnoDB, а значит, что их записи хранятся отдельно в файле ibdata* (в моем случае, только ibdata1). Выхожу на уровень выше и вижу этот заветный файл. Рядом также два лога движка InnoDB: ib_logfile0 и ib_logfile1. Открыв один из логов для просмотра в текстовом редакторе среди “кракозябр” я также увидел и куски самой информации, и тут я понял, что восстановить базу можно, по крайней мере, теоретически.

Надо было выбрать машину, где произвести попытку восстановления самой базы. Т.к. во время разработки я пользуюсь тестовым сервером, доступ к которому на уровне файловой системы мне без боя никто не даст, я решаю установить MySQL Server 5.1 и MySQL GUI Tools прямо на свою девелоперскую машину, работающую под Windows XP. Итак, все стоит, СУБД запущено, и подумав, как бы подступиться к задаче, я решаю сначала сделать экспорт скрипта создания БД с тестового сервера, и запускаю его потом на своей машине. Теперь у меня есть пустая база данных с полностью идентичной структурой и со всеми хранимыми процедурами. Останавливаю сервис MySQL, и копирую все содержимое из папки с именем нашей базы с упавшего сервера в аналогичную папку свежеустановленной локально у себя MySQL (папка с именем базы, вложенная в папку data, расположенную по пути для хранения файлов с данными указанному во время установки MySQL). При настройке локальной MySQL Server я решил указать для файлов данных InnoDB отдельную папку, куда далее скидываю восстановленный файл ibdata1. После этого перекидываю файлы InnoDB-логов (ib_logfile0 и ib_logfile1) прямо в свою папку data, где они должны быть по умолчанию.

Очередная попытка запустить MySQL снова не увенчалась успехом. Не имея реально боевого опыта в администрировании СУБД (за исключением опыта, полученного на курсах, но, опять же, по другим СУБД) я решил пойти путем эксперимента, хотя сейчас понимаю, что он совсем не был необходим. Удаляю файлы логов InnoDB, запускаю сервис MySQL и вижу, что он создает сами файлы InnoDB-логов с такими же названиями, но с размером в 20Мб.

Смотрю размер “своих” файлов с логами с упавшего сервера и вижу, что он существенно отличается и составляет 81Мб. Что-ж… останавливаю сервер, и захожу через MySQL Administrator в настройки переменных запуска (Startup Variables), вкладка InnoDB. Задаю размер log-файлов равным 81 Мб и закидываю обратно “свои” лог-файлы. MySQL запустился, но данных в интересующих меня таблицах я не увидел (хотя в таблицах, работавших на движке MyISAM, они уже были восстановлены). Немного пошарив по сети, я вычитал, что нужно запустить MySQL в режиме восстановления, для чего нужно задать значение стартовой переменно InnoDB_force_recovery равным 6. Попробовал сделать это через MySQL Administrator (ясное дело, что можно задавать параметры через GUI, а можно и редактировать файлы my.ini или my.cnf [в зависимости от версии MySQL и выбранной ОС] вручную), и, то ли я что-то не доделал, то ли что-то недосмотрел, но нужного результата не увидел. MySQL запустился как обычно, но про восстановление не сказал ни слова. “Что-ж,” – подумал я – “попробуем тогда через консоль, и еще раз явно пропишем параметр innodb_force_recovery”.

Запускаю коммандную строку Windows и ввожу:

> mysqld --console --innodb_force_recovery=6

На что в ответ получаю:

110727 10:31:52 [Note] Plugin 'FEDERATED' is disabled.
110727 10:31:52 InnoDB: Initializing buffer pool, size = 40.0M
110727 10:31:52 InnoDB: Completed initialization of buffer pool
110727 10:31:52 InnoDB: Operating system error number 32 in a file operation.
InnoDB: The error means that another program is using InnoDB's files.
InnoDB: This might be a backup or antivirus software or another instance
InnoDB: of MySQL. Please close it to get rid of this error.


Грешить на антивирус особого смысла не было, и я решил остановить выполнение комманды при помощи CTRL+break, затем повторить еще раз команду с включенным Task Manager. Перед запуском команды на выполнение я просмотрел список процессов, чтобы убедиться в том, что ни один экземпляр сервиса MySQL не запущен. И снова повторил команду. Увидев то же самое сообщение, я заглянул в Task Manager и увидел, что почему-то запущено сразу два процесса с именем mysqld, причем, один запущен как системный, а другой – от имени моего пользователя. Убив системный процесс, я “разорвал порочный круг”, и выполнение пошло дальше:

> mysqld --console --innodb_force_recovery=6

110727 10:31:52 [Note] Plugin 'FEDERATED' is disabled.
110727 10:31:52 InnoDB: Initializing buffer pool, size = 40.0M
110727 10:31:52 InnoDB: Completed initialization of buffer pool
110727 10:31:52 InnoDB: Operating system error number 32 in a file operation.
InnoDB: The error means that another program is using InnoDB's files.
InnoDB: This might be a backup or antivirus software or another instance
InnoDB: of MySQL. Please close it to get rid of this error.
110727 10:32:02 InnoDB: Operating system error number 32 in a file operation.
InnoDB: The error means that another program is using InnoDB's files.
InnoDB: This might be a backup or antivirus software or another instance
InnoDB: of MySQL. Please close it to get rid of this error.
110727 10:32:12 InnoDB: Operating system error number 32 in a file operation.
InnoDB: The error means that another program is using InnoDB's files.
InnoDB: This might be a backup or antivirus software or another instance
InnoDB: of MySQL. Please close it to get rid of this error.
InnoDB: The user has set SRV_FORCE_NO_LOG_REDO on
InnoDB: Skipping log redo
110727 10:32:22 InnoDB: Started; log sequence number 0 0
InnoDB: !!! InnoDB_force_recovery is set to 6 !!!
110727 10:32:22 [Note] Event Scheduler: Loaded 0 events
110727 10:32:22 [Note] mysqld: ready for connections.
Version: '5.1.58-community' socket: '' port: 3306 MySQL Community Server (GPL)


Пока что я не понял, в чем было дело, почему запускалось сразу два процесса. В том числе и потому, что радость охватила меня, и подключившись к базе данных через Toad for MySQL я увидел, что данные восстановленны! Эта радость отодвинула все вопросы на второй план.

Осознание победы и контрольный выстрел


“Не отходя от кассы”, я делаю дамп базы, чтобы впоследствии восстановить ее на рабочем сервере – том самом герое этой “сказки”, которому всего-то навсего поменяли жесткий диск и переустановили систему.

> mysqldump --routines -u "user" -p db_name > [path\]db_name.sql

Ввожу пароль, и получаю скрипт создания и заполнения всех таблиц базы, а также всех хранимых процедур в файле db_name.sql в указанной в параметре папке. Информация спасена!

Затем уже на рабочем сервере устанавливаю сам MySQL, настраиваю его, создаю пустую базу и скидываю в нее дамп:

> mysql -u "user" –p db_name < [path\]db_name.sql

В настройках клиентского приложения менять ничего не пришлось, т.к. ip-адрес и сетевое имя сервера остались теми же, что и были до падения. Зашли в систему, проверили, и – о, чудо! – вся информация на месте. Тут и сказочке конец… но остановимся на “морали сей басни".

Выводы и заключение


Для себя я в очередной раз понял несколько важных вещей, а именно:
  1. Стратегия бэкапа/восстановления должна определяться уже на стадии разработки;
  2. В вопросах бэкапа/восстановления не надо надеяться на админов, если можешь сделать это сам; и тем более, если для тебя не составит особого труда автоматизировать этот процесс (что вполне можно сделать на примитивном уровне штатными средствами);
  3. Организация на предприятии и четкое регламентирование обязанностей каждого отдельного сотрудника в каждом конкретном проекте очень важны, когда мы имеем дело с IT; и современные стандарты разделения труда на IT-предприятиях очень и очень актуальны и выросли не на пустом месте;
  4. И все-же, если база полетела и нет бэкапа – это еще не повод сразу отчаиваться и опускать руки.

Изначально я думал просто дать кратенькую инструкцию что и как делать в таком случае, но, во-первых, подобные инструкции, в принципе, имеются в сети; и, во-вторых, решил более подробно описать все как было, с “подводными камнями” и ошибками; а также счел, что подобная форма изложения может оказаться несколько интересней. На самом деле, я уверен, что с подобными подводными камнями (точнее, мелкими камушками) столкнется меньшинство, но тем не менее, считаю важным, чтобы они были описаны.

И, наконец, краткая памятка-рецепт, что делать в подобной ситуации, когда от рабочей базы с таблицами InnoDB остаются только файлы с данными и логами InnoDB:
  1. Подставляем в папку с данными файлы данных и описаний таблиц (файлы *.frm, *.MYI и *.MYD, все в папке с именем БД — т.е. копируем всю эту папку целиком);
  2. В папку c файлами данных InnoDB (по умолчанию, скорее всего, это папка data, где хранятся и бинарные логи, и папки с именами БД; в моем случае я сделал для данных InnoDB отдельную папку) скидываем все файлы ibdata*. Здесь, наверное, надо отметить, что размер файлов должен соответствовать указанному в настройках. Если размеры разнятся, то можно поступить так: удалить созданный сервером файл (или файлы) вручную, и создать новый (или, соответственно, новые) с размером равным размеру имеющегося, затем запустить сервер и остановить его, и вместо созданных им файлов подставить свои. Облегчить эту операцию – чтобы не пришлось играть с настройками вручную – может MySQL Administrator. Или можно сделать еще проще – элементарно подмениь все файлы (или файл, если он один), а в файле настроек my.ini/my.cfg просто изменить размер – по идее должно сработать.
  3. Скидываем логи InnoDB (или подменяем, если они уже есть) туда, где они должны быть (по умолчанию, это та же папка data, где хранятся файлы с данными и описаниями, но это, опять же, может быть изменено в настройках);
  4. Задаем размер логов InnoDB равным размеру наших логов (если вручную – то в байтах, если в MySQL Administrator – в Мб);
  5. Запускаем MySQL daemon
    > mysqld --console
    внимательно смотрим лог, должен быть запущен сервис и выполнено восстановление;
  6. Если восстановление не происходит, тогда в конфигурации или параметром ставим innodb_force_recovery=1 и пробуем запустить. Если не проходит, ставим innodb_force_recovery=2 и пробуем запустить. И т.д. до 6.
  7. И, наконец, смотрим на месте ли наши данные, и если на месте – делаем дамп базы, который потом уже разворачиваем на рабочий сервер.

Подобная операция в Linux или другой Unix-подобной системе будет выглядеть примерно так же. Кроме того, насколько я понимаю, файлы данных и логов MySQL (и бинарных логов, и логов InnoDB) универсальны для всех систем, и то, что упало на Windows, таким же образом можно восстановить и на Linux, и на OS X, и на любой другой системе, где работает сама MySQL.

Ссылки


Документация по движку InnoDB

В дополнение


PhantomTLT в комментарии подсказывает (и, опираясь на его советы, я немного скорректировал памятку выше):

Если у вас сохранился весь datadir, то никаких пустых баз с идентичной структурой создавать не нужно. Достаточно просто заменить все файлы и откорректировать размер логов InnoDB.

Далее стартуем MySQL и внимательно смотрим лог. Должен стартануть и выполнить восстановление.

Если восстановление не проходит, тогда в конфиге ставим innodb_force_recovery=1 (http://dev.mysql.com/doc/refman/5.1/en/forcing-innodb-recovery.html) и пробуем стартовать. Если не стартуем, ставим innodb_force_recovery=2 и пробуем стартовать. И т.д. до 6.

Если сразу стартуете с innodb_force_recovery=6, то вы рискуете получить БД в inconsistent state. Т.е. целостность (согласованность) данных может быть нарушена.

Если вы дошли до innodb_force_recovery=6 и это не помогло, то очень грустно — нужно восстанавливать в полуручном режиме.
Tags:
Hubs:
+52
Comments 25
Comments Comments 25

Articles