Восстановление данных в MySQL из снимка EBS

Это короткое руководство, возможно, поможет кому-то, кто использует AWS (и, в частности, MySQL на инстансе EC2), восстановить данные в MySQL из снимка EBS (регулярное создание которых любой предусмотрительный системный администратор, конечно же, настраивает заблаговременно — с помощью ec2‑consistent‑snapshot, например)Прежде всего нужно открыть EC2 Management Console, и в разделе ELASTIC BLOCK STORE → Snapshots найти подходящий снимок (обычно это последний снимок раздела).

Далее нужно щёлкнуть на снимке правой кнопкой мышки и выбрать «Create Volume». В Availability Zone при этом нужно выбрать тот же регион, в котором находится инстанс EC2.

После этого нужно перейти в раздел ELASTIC BLOCK STORE → Volumes и, опять же, щёлкнуть правой кнопкой мышки на появившийся раздел. В меню нужно выбрать пункт «Attach Volume», после чего в появившемся модальном окне выбрать инстанс EC2 и нажать «Yes, Attach».

Всё — на сервере должно появиться новое блочное устройство. Теперь можно запустить dmesg | tail и посмотреть, какой идентификатор был присвоен для подключённого блочного устройства. Допустим, это xvdg. Тогда ФС может располагаться, например, на /dev/xvdg1 (зависит от предпочтений того, кто создавал таблицу разделов).Создаём новый каталог и монтируем в него раздел:

mkdir /mnt/backup mount /dev/xvdg1 /mnt/backup Для того, чтобы получить необходимые данные из бэкапа, включим дополнительный экземпляр MySQL, работающий с отдельной директорией данных: sudo -u mysql /usr/libexec/mysqld --basedir=/usr --datadir=/mnt/backup/mysql --plugin-dir=/usr/lib64/mysql/plugin --log-error=/var/log/mysqld_backup.log --pid-file=/var/run/mysqld/mysqld_backup.pid --socket=/var/lib/mysql/mysql_backup.sock --port=5523 Теперь попробуем подключиться к запущенному экземпляру MySQL: mysql -h 127.0.0.1 -P 5523 Если подключиться удалось, можно начинать процесс восстановления данных.Для примера рассмотрим довольно простой сценарий: администратор-стажёр менял в продакшене значение для конкретного поля конкретного объекта, но отвлёкся на размышления о вечном, и случайно забыл написать в SQL‑запросе WHERE. Ну, ничего страшного — с кем не бывает.

Таким образом, теперь мы хотим восстановить значения поля sex в website.profile. Причём даже не для всех записей, а где-то для трети (потому что администратор был, конечно, задумчивый, но не настолько, чтобы не нажать Ctrl + C, поняв, что запрос явно выполняется подозрительно долго). Для этого в шелле основной базы данных создадим файл, содержащий нужные идентификаторы:

select id from profile where sex=«test» into outfile '/tmp/profile_id_list'; Соответственно, будет создан файл /tmp/profile_id_list, где будут идентификаторы тех записей, поле sex которых нужно восстановить из бэкапа.Далее пишем restore.py:

import MySQLdb

db = MySQLdb.connect (host=»127.0.0.1», port=5523, user=«user», passwd=«password», db=«website»)

c = db.cursor ()

f = open (»/tmp/profile_id_list»)

for profile_id in f.readlines (): c.execute ( «select sex from profile where id=%s», (profile_id,) ) print «update profile set sex=\»%s\» where id=%s;» % ( c.fetchone ()[0], profile_id[:-1] ) И записываем SQL‑файл для восстановления sex: python restore.py > restore.sql Проверяем, что файл в порядке (например, количество строк можно посмотреть с помощью wc -l restore.sql), и, затем, выполняем SQL‑запросы из файла: mysql website < restore.sql Проверяем, что всё успешно восстановилось.Теперь можно удалить /tmp/profile_id_list и прочие файлы, и, соответственно, выключить MySQL‑сервер:

mysqladmin -u root -p -h 127.0.0.1 -P 5523 shutdown Далее просто отмонтируем раздел и удалим каталог, в который он монтировался: umount /mnt/backup rm -r /mnt/backup А в AWS Management Console, соотвественно, заходим в раздел ELASTIC BLOCK STORE → Volumes и отключаем виртуальное блочное устройство (Detach Volume). После этого его можно удалить (Delete Volume).Ещё можно снова зайти в раздел со снимками (ELASTIC BLOCK STORE → Snapshots) и как-нибудь отметить те снимки, где (судя по времени создания снимка) есть неправильные данные (например, отразить это в имени снимка). Альтернатива — вообще удалить снимок. Но это решение хуже с той точки зрения, что именно этот снимок может понадобиться кому-то ещё (для восстановления совершенно других данных, которые именно в этом снимке могут быть как раз в полном порядке). Поэтому лучше по умолчанию предполагать, что любой современный (а тем более самый последний) снимок может содержать ценные данные, и на всякий случай не удалять их какое-то время (например, неделю).

И напоследок совет. У MySQL есть режим, в котором нельзя выполнить запрос DELETE или UPDATE, если в нём не указано условие WHERE, в котором однозначно задаётся конкретный объект. Поэтому, если вы случайно забыли добавить WHERE, то вы просто получите ошибку:

ERROR 1175 (HY000): You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column

Для включения такого режима достаточно добавить к опциям команды mysql, по вкусу: ‑‑i‑am‑a‑dummy или ‑‑safe‑updates.Аналогичного эффекта можно добиться, добавив в файл ~/.my.cnf строку safe‑updates (что удобно, например, если вы запускаете команду mysql вообще без всяких опций, и всё автоматически берётся из ~/.my.cnf).

Кстати, по умолчанию этот режим добавляет ещё пару ограничений (которые, впрочем, можно отключить, но на практике это требуется редко): для select_limit устанавливается значение 1000, а для max_join_size — 1000000.

© Habrahabr.ru