Simple DB backup

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

(Различия между версиями)
Перейти к: навигация, поиск
Строка 311: Строка 311:
Следует с горечью отметить ,что фича "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 уже в этом тысячилетии.
Следует с горечью отметить ,что фича "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 делать вызов этого статемента самим.

Версия 08:48, 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 таблиц. Поэтому таблицы с помесячными логами таким образом восстановить не удасться. Придётся выходить из положения старым способом (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 делать вызов этого статемента самим.

Личные инструменты