Simple DB backup

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

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