Simple DB backup

Материал из BiTel WiKi

Перейти к: навигация, поиск

Большое спасибо пользователю 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 таблиц. Поэтому таблицы с помесячными логами таким образом восстановить не удасться. Придётся выходить из положения старым способом (http://www.chriscalender.com/?p=28) или использовать percona mysql server или дожидаться выхода 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/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 --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
Личные инструменты