Как стать автором
Обновить

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

Время на прочтение 4 мин
Количество просмотров 4K
Это короткое руководство, возможно, поможет кому-то, кто использует 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.
Теги:
Хабы:
+14
Комментарии 3
Комментарии Комментарии 3

Публикации

Истории

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн