Database backup
Материал из BiTel WiKi
Проблема: резервирвание базы данных объемом несколько десятков или сотен гигабайт без остановки сервиса.
Можно использовать "взрослые" накопители типа NAS/SAN, которые имеют встроенную поддержку снапшотов и резервного копирования информации, но это решения дорогостоящие (хотя, что может быть ценнее информации, которая является основой для расчетов с абонентами? вопрос риторический). Поэтому мы пойдем "пионерским" путем. Нам потребуется обычный linux с поддержкой lvm/lvm2. О том, как подготовить дисковое хранилище для размещения базы, я тут особо распространяться не буду. Желающие всегда могут почитать man'ы (pvcreate/vgcreate/lvcreate сотоварищи). Замечу только, что при создании logical volume для хранения базы необходимо оставлять порядка 2-4% нераспределенных physical extents для того, чтобы lvm было куда писать измененные данные во время бекапа. Пример:
# vgdisplay vg1 --- Volume group --- VG Name vg1 System ID Format lvm2 Metadata Areas 1 Metadata Sequence No 3201 VG Access read/write VG Status resizable MAX LV 0 Cur LV 1 Open LV 1 Max PV 0 Cur PV 1 Act PV 1 VG Size 271.94 GB PE Size 32.00 MB Total PE 8702 Alloc PE / Size 8650 / 270.31 GB Free PE / Size 52 / 1.62 GB VG UUID LgXAR3-SPn6-skB8-O0Y0-HI8N-o6gh-0Zy7qc
Здесь я оставил нераспределенными 52 extent'а, что в сумме дает 1.6 гигабайт. Этого вполне хватает для работы базы в течение получаса. Если не хочется рисковать - можно увеличить процент нераспределенных extent'ов.
Итак, предположим, что хранилище под базу соответствует соглашению: оно находится на logical volume, и volume group имеет достаточно нераспределенных extent'ов. Далее все, что нам нужно - это написать пару скриптов, которые будут создавать снапшот и копировать информацию. Кроме штатных средств (lvcreate, lvremove) нам потребуется пакет rsync. Хочется сразу отметить, что полный бекап базы объемом пару десятков гигабайт может занять несколько часов, поэтому мы будем использовать для копирования rsync, который позволяет копировать только измененные файлы (и даже только части измененных файлов). Это означает, что на момент бекапа где-то (там, куда мы копируем текущую базу) уже должна быть хотя бы частично скопированная база, актуальная на какой-то момент в прошлом. Это также означает, что мы имеем всего лишь один бекап - по состоянию на момент последнего резервирования (то есть, если бекап производится раз в сутки - бекап за прошлые сутки). Эту схему, конечно, можно модифицировать, но упражнение по модификации пусть будет вашим домашним заданием. Мы же решаем вполне конкретную задачу: получить консистентную копию базы без прерывания сервиса.
Итак, скрипт №1 (/usr/local/libexec/backupdb/backup.sh):
#!/bin/sh lvcreate=/usr/sbin/lvcreate lvremove=/usr/sbin/lvremove mysql=/usr/bin/mysql mount=/bin/mount umount=/bin/umount mysqladmin=/usr/bin/mysqladmin rsync=/usr/bin/rsync myisamchk=/usr/bin/myisamchk snapscript=/usr/local/libexec/backupdb/makesnap.sh nice=/bin/nice date=/bin/date created=0 echo $($date '+%Y-%m-%d %H:%M:%S') creating snapshot $mysql <<-LCK FLUSH TABLES WITH READ LOCK; system /bin/sh $snapscript UNLOCK TABLES; LCK echo $($date '+%Y-%m-%d %H:%M:%S') mounting snapshot $mount -r /dev/vg1/snap /snapshot echo $($date '+%Y-%m-%d %H:%M:%S') mounting backup storage $mount /backup echo $($date '+%Y-%m-%d %H:%M:%S') rsyncing... $nice -n 19 $rsync -aH --bwlimit=25000 --delete /snapshot/ /backup/ echo $($date '+%Y-%m-%d %H:%M:%S') unmounting snapshot $umount /snapshot echo $($date '+%Y-%m-%d %H:%M:%S') removing snapshot $lvremove -f vg1/snap >/dev/null 2>&1 echo $($date '+%Y-%m-%d %H:%M:%S') unmounting backup storage $umount /backup
Скрипт №2 (/usr/local/libexec/backupdb/makesnap.sh):
#!/bin/sh lvcreate=/usr/sbin/lvcreate lvremove=/usr/sbin/lvremove sleep=/bin/sleep sync=/bin/sync created=0 while [ $created -eq 0 ] ; do $sync ; $sync $lvcreate -s -l 52 -Mn -n snap vg1/storage >/dev/null 2>&1 if [ $? -eq 0 ] ; then created=1 else $lvremove -f vg1/snap >/dev/null 2>&1 fi $sleep 1 done
Здесь:
- /dev/vg1
- volume group, на которой у меня располагается logical volume "storage" (хранилище базы данных)
- /dev/vg1/storage
- собственно логический том "storage"
- /dev/vg1/snap
- снапшот этого логического тома
- /backup/
- раздел, где располагается копия базы данных, для простоты я внес раздел в /etc/fstab с опцией noauto
Зачем так сложно, в два скрипта? Почему не сделать один? Все просто: база должна быть консистентной. Этого можно добиться только временно заблокировав все таблицы на момент снятия снапшота, чем и занимается скрипт №2.
Кстати, хозяйке на заметку. Если по какой-то причине скрипт №2 не сможет сделать снапшот, он заблокирует работу MySQL перманентно, до прерывания. В качестве домашнего задания модифицируйте скрипты так, чтобы скрипт №2 выполнял не более, скажем, 30 попыток сделать снапшот и в случае неудачи всех попыток скрипт №1 вместо удаления копии базы завершал работу с корректной диагностикой. Правда, я столкнулся с такой ситуацией всего один раз, но это не значит, что креститься надо строго после того как грянет гром. =)
Обратите внимание на строчку создания снапшота в скриипте №2. Я задействовал все свободные PE из группы (52 штуки). Как говорится, YMMV.
После этого делаем примерно так:
# ln -s /usr/local/libexec/backupdb/backup.sh /etc/cron.daily/backupdb
И еще нам нужно подготовить первичную копию базы для последующего бекапа. Это можно сделать все тем же rsync'ом:
# mount /backup # rsync -aH --bwlimit=10000 /storage/ /backup/ # umount /backup
Обратите внимание на два момента:
- Я ограничиваю скорость копирования опцией bwlimit. Это необходимо для того, чтобы не нарушать нормальную работу базы
- Нас не заботит консистентность базы. Все равно очередной бекап "доведет" ее до состояния консистентности благодаря возможностям rsync
--boco 10:36, 10 июня 2008 (UTC)