Это короткое руководство, возможно, поможет кому-то, кто использует 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».
Всё — на сервере должно появиться новое блочное устройство. Теперь можно запустить
Создаём новый каталог и монтируем в него раздел:
Для того, чтобы получить необходимые данные из бэкапа, включим дополнительный экземпляр MySQL, работающий с отдельной директорией данных:
Теперь попробуем подключиться к запущенному экземпляру MySQL:
Если подключиться удалось, можно начинать процесс восстановления данных.
Для примера рассмотрим довольно простой сценарий: администратор-стажёр менял в продакшене значение для конкретного поля конкретного объекта, но отвлёкся на размышления о вечном, и случайно забыл написать в SQL‑запросе WHERE. Ну, ничего страшного — с кем не бывает.
Таким образом, теперь мы хотим восстановить значения поля sex в website.profile. Причём даже не для всех записей, а где-то для трети (потому что администратор был, конечно, задумчивый, но не настолько, чтобы не нажать Ctrl + C, поняв, что запрос явно выполняется подозрительно долго). Для этого в шелле основной базы данных создадим файл, содержащий нужные идентификаторы:
Соответственно, будет создан файл /tmp/profile_id_list, где будут идентификаторы тех записей, поле sex которых нужно восстановить из бэкапа.
Далее пишем вот такой сценарий, и сохраняем его под именем restore.py:
И записываем SQL‑файл для восстановления sex:
Проверяем, что файл в порядке (например, количество строк можно посмотреть с помощью
Проверяем, что всё успешно восстановилось.
Теперь можно удалить /tmp/profile_id_list и прочие файлы, и, соответственно, выключить MySQL‑сервер:
Далее просто отмонтируем раздел и удалим каталог, в который он монтировался:
А в AWS Management Console, соответственно, заходим в раздел ELASTIC BLOCK STORE → Volumes и отключаем виртуальное блочное устройство (Detach Volume). После этого его можно удалить (Delete Volume).
Ещё можно снова зайти в раздел со снимками (ELASTIC BLOCK STORE → Snapshots) и как-нибудь отметить те снимки, где (судя по времени создания снимка) есть неправильные данные (например, отразить это в имени снимка). Альтернатива — вообще удалить снимок. Но это решение хуже с той точки зрения, что именно этот снимок может понадобиться кому-то ещё (для восстановления совершенно других данных, которые именно в этом снимке могут быть как раз в полном порядке). Поэтому лучше по умолчанию предполагать, что любой современный (а тем более самый последний) снимок может содержать ценные данные, и на всякий случай не удалять их какое-то время (например, неделю).
И напоследок совет. У MySQL есть режим, в котором нельзя выполнить запрос DELETE или UPDATE, если в нём не указано условие WHERE, в котором однозначно задаётся конкретный объект. Поэтому, если вы случайно забыли добавить WHERE, то вы просто получите ошибку:
Аналогичного эффекта можно добиться, добавив в файл ~/.my.cnf строку
Кстати, по умолчанию этот режим добавляет ещё пару ограничений (которые, впрочем, можно отключить, но на практике это требуется редко): для select_limit устанавливается значение 1000, а для max_join_size — 1000000.
Прежде всего нужно открыть 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.