Simple DB backup
Материал из BiTel WiKi
Ok-2004 (Обсуждение | вклад) |
Ok-2004 (Обсуждение | вклад) |
||
Строка 311: | Строка 311: | ||
Следует с горечью отметить ,что фича "transportable tablespaces" в mysql 5.6 реализована частично и не поддерживает export/import partition-based innodb таблиц. Поэтому таблицы с помесячными логами восстановить не удасться без плясок с бубном типа такого: | Следует с горечью отметить ,что фича "transportable tablespaces" в mysql 5.6 реализована частично и не поддерживает export/import partition-based innodb таблиц. Поэтому таблицы с помесячными логами восстановить не удасться без плясок с бубном типа такого: | ||
- | Допустим надо отремонтировать | + | |
+ | Допустим надо отремонтировать "партицированную" таблицу "inet_session_10" : | ||
<source lang="bash"> | <source lang="bash"> |
Текущая версия на 11:21, 10 февраля 2015
Большое спасибо пользователю ok-2004 с форума битела за помощь
UPDATE: Схема проверена и годится для переноса DB на другой сервер.
Используем Percona XtraBackup
Собирать и компилить ничего не надо - качаем binary, просто кидаем содержимое в каталог типа /usr/sbin/ что бы пути не писать.
cat /etc/rc.d/rc.full.backup
#!/bin/bash s_date=`date "+%Y-%m-%d_%H-%M-%S"` s_date_log=$s_date'_log' innobackupex \ --defaults-file=/var/lib/mysql/my.cnf.b \ --no-lock \ --no-timestamp \ --ibbackup=/usr/bin/xtrabackup_55 \ --user=root \ --password=mypassword \ /var/BACKUP/$s_date > /var/BACKUP/$s_date_log 2>&1 cp /var/lib/mysql/my.cnf /var/BACKUP/$s_date tar -cvzf /var/BACKUP/$s_date.tar.gz /var/BACKUP/$s_date >/dev/null 2>&1 rm -rf /var/BACKUP/$s_date
внимание на my.cnf.b - это тот же my.cnf, только пути проставлены полные, типа:
datadir=/var/lib/mysql/db
innodb_data_home_dir = /var/lib/mysql/db
innodb_log_group_home_dir = /var/lib/mysql/log
Перед восстановлением, развернув из архива, бекап надо подготовить:
cat prepare.backup.sh
#!/bin/bash log=$1'_log' innobackupex \ --use-memory=1G \ --defaults-file=/var/lib/mysql/my.cnf.b \ --apply-log \ --ibbackup=xtrabackup_55 \ --user=root \ --password=mypassword \ /var/BACKUP/$1 > /var/BACKUP/$log 2>&1
Само восстановление проводить лучше уже руками:
бездумно не стоит копировать эти строки
#/etc/init.d/mysql stop #rm -rf /var/lib/mysql/db #mkdir /var/lib/mysql/db #cp -av /var/BACKUP/full/bgbilling /var/lib/mysql/db #cp -av /var/BACKUP/full/mysql /var/lib/mysql/db #cp -av /var/BACKUP/full/performance_schema /var/lib/mysql/db #cp -av /var/BACKUP/full/ibd* /var/lib/mysql/db #chown -R mysql:mysql /var/lib/mysql/db #chown -R mysql:mysql /var/lib/mysql/log #chmod -R 775 /var/lib/mysql/ #/etc/init.d/mysql start
P.S.:
Следует отметить, что если ls -al /var/lib/mysql/bgbilling/*.ibd|wc -l будет больше 1024 - процесс бэкапа завершиться с ошибкой. Workaround этого находится в начале следущего "боевого скрипта" который бэкапит базу с числом таких файлов около 1837....:
#!/bin/bash #/var/bgb/full - временной каталог для бэкапов #/var/sos/ - подмонтированный через сеть, некий райд-массив... #боремся с досадным багом перконы ulimit -n 4096 d=`date +%d%m%g_%M%H` echo "$d" if [ -f /var/bgb/stop ] then #Предыдущий бэкап сбойнул, уходим отсюда... echo "STOP FOUND !" echo "$d stop found !" >> /var/bgb/xtra_log #Запускаем на компе админа со всего размаху алерт ему в консоль: export SSH_ASKPASS="/root/p.sh" export DISPLAY=":0" #ссашимся на комп админа без пароля но и без предварительного обмена ключами: ##cat p.sh #!/bin/bash #echo "МойСамыйСекретныйПарольНаКомпАдмина" setsid ssh admin@ip.компа.админа "wall \"А у вас очередной бэкап не прошёл... ;) ! \"" exit 1 fi ############################################################# #Начинаем и навсякий случай сразу напишем завещание touch /var/bgb/stop #Расчищаем место : if [ -d /var/bgb/full ] ; then rm -rf /var/bgb/full fi if [ -f /var/bgb/full_log ] ; then rm -f /var/bgb/full_log fi if [ -f /var/bgb/prep_log ] ; then rm -f /var/bgb/prep_log fi if [ -f /var/bgb/full_ok ] ; then rm -f /var/bgb/full_ok fi if [ -f /var/bgb/prep_ok ] ; then rm -f /var/bgb/prep_ok fi # echo "FULL BEGIN!" innobackupex \ --defaults-file=/etc/mysql/my.cnf \ --no-lock \ --no-timestamp \ --ibbackup=/usr/bin/xtrabackup_55 \ --user=root \ --password=xxxxx \ /var/bgb/full > /var/bgb/full_log 2>&1 if [ -z "`tail -1 /var/bgb/full_log | grep 'completed OK!'`" ] then #Чота с базой нето... валим отсюда, завещание оставляем echo "$d full fail!" >> /var/bgb/xtra_log echo "FULL FAIL!" exit 1 else touch /var/bgb/full_ok echo "FULL OK!" fi ############################################################################## if [ -f /var/bgb/full_ok ] && [ -d /var/bgb/full ] then #Вродь бэкап сработал, значит начинаем его приводить в человеческий вид echo "PREP BEGIN!" innobackupex \ --use-memory=1G \ --defaults-file=/etc/mysql/my.cnf \ --apply-log \ --ibbackup=xtrabackup_55 \ --user=root \ --password=xxxxx \ /var/bgb/full > /var/bgb/prep_log 2>&1 if [ -z "`tail -1 /var/bgb/prep_log | grep 'completed OK!'`" ] then #сбойнуло, валим отсюда, завещание адмниу оставляем... echo "$d prep fail!" >> /var/bgb/xtra_log echo "PREP FAIL!" exit 1 else touch /var/bgb/prep_ok echo "PREP OK!" fi else echo "$d full_ok not found!" >> /var/bgb/xtra_log echo "FULL_OK NOT FOUND!" exit 1 fi ################################################################################## if [ -f /var/bgb/prep_ok ] && [ -d /var/bgb/full ] then #Бэкап вродь успешен , начинаем копировать на ремотный стораж... echo "COPY BEGIN!...." space=`df -h /var/sos|awk '{print $5}'|grep -v Use|cut -d % -f1 -` case $space in [1-8]*|9) echo "/VAR/SOS almost full!" ;; 9[1-9]) echo "/var/sos/ full !" >> /var/bgb/xtra_log echo "/VAR/SOS/ cleaning...!" for d_i in `find /var/sos/ -maxdepth 1 -type d \( ! -iname "lost+found" ! -name "*.bak" \) -mtime +30 -print` do echo "$d_i" read -s -t5 -n1 -r -p "del ? OK ?" key if [ $? -eq 0 ]; then case $key in [Yy]* ) rm -rf "$d_i"; ;; * ) echo "$d_i will not deleted" ;; esac fi done #find /var/sos/ -maxdepth 1 -name "full_*" -type d -mtime +17 -exec rm -rf "{}" \; ;; esac rm -f /var/sos/cp_* if [ -d /var/sos/full ] then mv /var/sos/full /var/sos/full_$d fi cp -av /var/bgb/full /var/sos > /var/sos/cp_log 2>&1 if [ $? != 0 ] then #Чота сегодня не копируется, блин.... echo "$d copy fail!" >> /var/bgb/xtra_log echo "COPY FAIL!" exit 1 else echo "COPY OK!" #Ну вродь все! Завещание рвём! rm -f /var/bgb/stop fi else echo "$d prep_ok not found!" >> /var/bgb/xtra_log echo "PREP_OK NOT FOUND!" exit fi
P.S.2 Вариант и использованием замечательного свойства percona xtrabackup делать "инкрементальные" бэкапы. За день делаются 24 почасовых бэкапа в каталоге "var/bgb/". Отличительным свойством этого скрипта является то, что резервная копия базы почти сразу готова к использованию ( кроме последнего шага, отмеченнного в скрипте с комментариями "!1!" и "!2!".)
#!/bin/bash #set -x #бэкапим базу с такими ключами: ARGS_B="--defaults-file=/etc/mysql/my.cnf --no-lock --no-timestamp --ibbackup=/usr/bin/xtrabackup_55 --user=root --password=xxxxx" #"препарим" c такими: ARGS_P="--use-memory=1G --defaults-file=/etc/mysql/my.cnf --apply-log --ibbackup=xtrabackup_55 --user=root --password=xxxxx" #в файле ордер храним порядковый номер бэкапа в течение дня. (от 0 до 23) if [ -f /var/bgb/order ] then source /var/bgb/order else echo "order=0" > /var/bgb/order order=0 fi if [[ $order -eq 0 ]] then # в каталоге inc_0 храним полный бэкап в 0 часов, в каталогах inc_1... inc_23 - почасовые инкрементальные. rm -rf /var/bgb/inc_0 rm -rf /var/bgb/inc_0.bak rm -f /var/bgb/*_log innobackupex ${ARGS_B}|> /var/bgb/inc_0 > /var/bgb/inc_0_log 2>&1 # Навсякий случай делаем копию. cp -a /var/bgb/inc_0 /var/bgb/inc_0.bak # "сбэкапили" и тутже частично "препарим" дабы не тратить время когда, настанет час "X" innobackupex ${ARGS_P}|> --redo-only /var/bgb/inc_0 > /var/bgb/prep_0_log 2>&1 echo "order=1" > /var/bgb/order else order_next=$((order+1)) order_prev=$((order-1)) rm -rf /var/bgb/inc_${order}|> rm -rf /var/bgb/inc_${order}|>.bak #бэкапим инкрементально innobackupex ${ARGS_B}|> --incremental /var/bgb/inc_${order}|> --incremental-basedir=/var/bgb/inc_${order_prev}|> > /var/bgb/inc_${order}|>_log 2>&1 cp -a /var/bgb/inc_${order}|> /var/bgb/inc_${order}|>.bak case $order in [1-9]|1[0-9]|2[0-2]) # все промежуточные инкрементальные бэкапы "препарим" частично. innobackupex ${ARGS_P}|> --redo-only /var/bgb/inc_0 --incremental-dir=/var/bgb/inc_${order}|> > /var/bgb/prep_${order}|>_log 2>&1 echo "order=$order_next" > /var/bgb/order ;; 23) # !1! последний дневной бэкап "препарим" полностью. innobackupex ${ARGS_P}|> /var/bgb/inc_0 --incremental-dir=/var/bgback/inc_${order}|> > /var/bgb/prep_${order}|>_log 2>&1 # !2! последний "prepare" - полный, чтобы создать логи транзакций iblog*. innobackupex ${ARGS_P}|> /var/bgb/inc_0 > /var/bgb/prep_0_${order}|>_log 2>&1 echo "order=0" > /var/bgb/order ;; esac fi
Счастливым обладателям mysql 5.6 ( обладающим свойством transportable tablespaces ) можно изменить строку под комментарием "#!2!..." в виде:
innobackupex ${ARGS_P} --export /var/bgb/exp /var/bgb/inc_0 > /var/bgb/prep_0_${order}_log 2>&1.
В этом случае в каталоге /var/bgb/exp появятся индивидуальные экспортные копии каждой таблицы базы, пригодные для восстановления отдельной таблицы innodb:
0. DROP TABLE crashed_table; 1. CREATE TABLE crashed_table (...) ENGINE=InnoDB; 2. ALTER TABLE crashed_table DISCARD TABLESPACE; 3. cp crashed_table.{ibd,exp,cfg } /var/lib/mysql/bgbilling 4. ALTER TABLE crashed_table IMPORT TABLESPACE;
вот тока надо знать структуру каждой таблицы, дабы воспользоваться ей на этапе 1. для этого поможет это скрипт:
#!/bin/bash mysql -u root -pxxxxx --skip-column-names -e "SELECT table_name FROM information_schema.tables WHERE table_schema = 'bgbilling';" > tables.txt cat /dev/null > tables_info.txt for x in `cat tables.txt` do echo "=========================" >> tables_info.txt echo "show create table $x;"|mysql -u root -pxxxxx -N bgbilling >> tables_info.txt done
Следует с горечью отметить ,что фича "transportable tablespaces" в mysql 5.6 реализована частично и не поддерживает export/import partition-based innodb таблиц. Поэтому таблицы с помесячными логами восстановить не удасться без плясок с бубном типа такого:
Допустим надо отремонтировать "партицированную" таблицу "inet_session_10" :
CREATE TABLE `inet_session_10` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `parentId` bigint(20) NOT NULL, `splittedId` bigint(20) NOT NULL, `connectionId` bigint(20) NOT NULL, `sessionStart` datetime NOT NULL, `sessionStop` datetime DEFAULT NULL, `lastActive` datetime NOT NULL, `sessionTime` bigint(20) NOT NULL, `sessionCost` decimal(12,5) NOT NULL, `deviceState` smallint(6) NOT NULL DEFAULT '1', `status` smallint(6) NOT NULL, KEY `start` (`sessionStart`), KEY `id` (`id`), KEY `parentId` (`parentId`), KEY `connectionId` (`connectionId`) ) ENGINE=InnoDB AUTO_INCREMENT=3107890 DEFAULT CHARSET=cp1251 /*!50100 PARTITION BY HASH (connectionId) PARTITIONS 8 */
Представленную на диске в виде:
/var/lib/mysql/bgbilling/inet_session_10.frm /var/lib/mysql/bgbilling/inet_session_10.par /var/lib/mysql/bgbilling/inet_session_10#P#p0.ibd /var/lib/mysql/bgbilling/inet_session_10#P#p1.ibd /var/lib/mysql/bgbilling/inet_session_10#P#p2.ibd /var/lib/mysql/bgbilling/inet_session_10#P#p3.ibd /var/lib/mysql/bgbilling/inet_session_10#P#p4.ibd /var/lib/mysql/bgbilling/inet_session_10#P#p5.ibd /var/lib/mysql/bgbilling/inet_session_10#P#p6.ibd /var/lib/mysql/bgbilling/inet_session_10#P#p7.ibd
Начнём:
CREATE TABLE _inet_session_10 LIKE inet_session_10; ALTER TABLE _inet_session_10 REMOVE PARTITIONING; ALTER TABLE _inet_session_10 DISCARD TABLESPACE; \!cp /var/bgb/0/bgbilling/inet_session_10#P#p1.exp /var/lib/mysql/_inet_session_10.exp \!cp /var/bgb/0/bgbilling/inet_session_10#P#p0.ibd /var/lib/mysql/_inet_session_10.ibd ALTER TABLE _inet_session_10 IMPORT TABLESPACE; ALTER TABLE inet_session_10 EXCHANGE PARTITION p0 WITH TABLE _inet_session_10; ALTER TABLE _inet_session_10 DISCARD TABLESPACE; \!cp /var/bgb/0/bgbilling/inet_session_10#P#p1.exp /var/lib/mysql/_inet_session_10.exp \!cp /var/bgb/0/bgbilling/inet_session_10#P#p1.ibd /var/lib/mysql/_inet_session_10.ibd ALTER TABLE _inet_session_10 IMPORT TABLESPACE; ALTER TABLE inet_session_10 EXCHANGE PARTITION p1 WITH TABLE _inet_session_10; ALTER TABLE _inet_session_10 DISCARD TABLESPACE; \!cp /var/bgb/0/bgbilling/inet_session_10#P#p2.exp /var/lib/mysql/_inet_session_10.exp \!cp /var/bgb/0/bgbilling/inet_session_10#P#p2.ibd /var/lib/mysql/_inet_session_10.ibd ALTER TABLE _inet_session_10 IMPORT TABLESPACE; ALTER TABLE inet_session_10 EXCHANGE PARTITION p2 WITH TABLE _inet_session_10; ............ ............ ALTER TABLE _inet_session_10 DISCARD TABLESPACE; \!cp /var/bgb/0/bgbilling/inet_session_10#P#p7.exp /var/lib/mysql/_inet_session_10.exp \!cp /var/bgb/0/bgbilling/inet_session_10#P#p7.ibd /var/lib/mysql/_inet_session_10.ibd ALTER TABLE _inet_session_10 IMPORT TABLESPACE; ALTER TABLE inet_session_10 EXCHANGE PARTITION p7 WITH TABLE _inet_session_10; DROP TABLE _inet_session_10;
Эта затея будет работать пока разработчики не введут в базе subpartitions Если подобный способ вводит в уныние ( ведь в базе есть таблицы с 31-ой партицией ), то дожидаться выхода oracle community mysql 5.7, в котором эта фича реализована полностью и который получит статус GA уже в этом тысячилетии.
P.S.3 При использовании инкрементальных бэкапов надо иметь ввиду что каждый инкрементальный бэкап копирует ВСЕ! таблицы *.ibd Вашей базы. Даже если LSN в ихних pages меньше LSN , зафиксированного последним инкрементальным бэкапом. При этом в "неизменившихся" таблицах копируется тока одна page (~16 K ), содержащая тока заголовок таблицы. Это зделано для того чтобы корректно обыгрывать ситуации когда между соседними инкрементальными бэкапами проскочила какая-нибудь DDL-операция, изменяющая сами таблицы, но не данные в них. Если таблиц много то сминимизировать время этого процесса можно включив с конфиги сервера "innodb_track_changed_pages = 1" ( если у Вас конечно Percona Server 5.5(6).
После выполнения очередного инкрементального бэкапа необходимо глянуть в файл xtrabackup-checkpoints и запомнить чему равно значение to_lsn = XXX дабы подчистить на винте место для следующих логов статементом PURGE CHANGED_PAGE_BITMAPS BEFORE XXX+1 В Percona Xtrabackup есть досаднейшая ошибка ( точнее ошибка в её файле-враппере innobackupex ) . Если этот скрипт запускать с опцией --no-lock ( что чаще всего и делают, если все базы на движке innodb ) то этот скрипт не делает перед вызовом xtrabackup статемента FLUSH CHANGED_PAGE_BITMAPS и процесс инкрементального бэкапа через innobackupex c использованием innodb_track_changed_pages сделает бэкапы "неконсистентными". Это сразу будет видно при последующей стадии prepare. Поэтому желательно перед каждым вызовом xtrabackup делать вызов этого статемента самим. Вот скрипт, до "посинения" делающий почасовые инкрементальные бэкапы:
#!/bin/bash while : do rm -rf /var/bgb ; mkdir /var/bgb mysql --login-path=local -e "FLUSH CHANGED_PAGE_BITMAPS;" innobackupex --defaults-file=/etc/mysql/my.cnf --no-lock --no-timestamp --user=root --password=XXXXXXXXXXXXXXX /var/bgb/0 > /var/bgb/log_b_0 2>&1 to_lsn=`grep to_lsn /var/bgb/0/xtrabackup_checkpoints | awk '{ print $3 }'` mysql --login-path=local -e "PURGE CHANGED_PAGE_BITMAPS BEFORE $to_lsn;" innobackupex --use-memory=1G --defaults-file=/etc/mysql/my.cnf --apply-log --redo-only --user=root --password=XXXXXXXXXXXXXXX /var/bgb/0 > /var/bgb/log_p_0 2>&1 sleep -m 60 mysql --login-path=local -e "FLUSH CHANGED_PAGE_BITMAPS;" innobackupex --defaults-file=/etc/mysql/my.cnf --no-lock --no-timestamp --user=root --password=XXXXXXXXXXXXXXX --incremental /var/bgb/1 --incremental-basedir=/var/bgb/0 > /var/bgb/log_b_1 2>&1 to_lsn=`grep to_lsn /var/bgb/1/xtrabackup_checkpoints | awk '{ print $3 }'` mysql --login-path=local -e "PURGE CHANGED_PAGE_BITMAPS BEFORE $to_lsn;" innobackupex --use-memory=1G --defaults-file=/etc/mysql/my.cnf --apply-log --redo-only --user=root --password=XXXXXXXXXXXXXXX /var/bgb/0 --incremental-dir=/var/bgb/1 > /var/bgb/log_p_1 2>&1 sleep -m 60 mysql --login-path=local -e "FLUSH CHANGED_PAGE_BITMAPS;" innobackupex --defaults-file=/etc/mysql/my.cnf --no-lock --no-timestamp --user=root --password=XXXXXXXXXXXXXXX --incremental /var/bgb/2 --incremental-basedir=/var/bgb/1 > /var/bgb/log_b_2 2>&1 to_lsn=`grep to_lsn /var/bgb/2/xtrabackup_checkpoints | awk '{ print $3 }'` mysql --login-path=local -e "PURGE CHANGED_PAGE_BITMAPS BEFORE $to_lsn;" innobackupex --use-memory=1G --defaults-file=/etc/mysql/my.cnf --apply-log --redo-only --user=root --password=XXXXXXXXXXXXXXX /var/bgb/0 --incremental-dir=/var/bgb/2 > /var/bgb/log_p_2 2>&1 #и так до 23-го бэкапа: sleep -m 60 mysql --login-path=local -e "FLUSH CHANGED_PAGE_BITMAPS;" innobackupex --defaults-file=/etc/mysql/my.cnf --no-lock --no-timestamp --user=root --password=XXXXXXXXXXXXXXX --incremental /var/bgb/23 --incremental-basedir=/var/bgb/22 > /var/bgb/log_b_23 2>&1 to_lsn=`grep to_lsn /var/bgb/23/xtrabackup_checkpoints | awk '{ print $3 }'` mysql --login-path=local -e "PURGE CHANGED_PAGE_BITMAPS BEFORE $to_lsn;" innobackupex --use-memory=1G --defaults-file=/etc/mysql/my.cnf --apply-log --user=root --password=XXXXXXXXXXXXXXX /var/bgb/0 --incremental-dir=/var/bgb/23 > /var/bgb/log_p_23 2>&1 innobackupex --use-memory=1G --defaults-file=/etc/mysql/my.cnf --apply-log --user=root --password=XXXXXXXXXXXXXXX /var/bgb/0 > /var/bgb/log_fin 2>&1 done