Как стать автором
Обновить

Простой скрипт для инкрементального бэкапа MySQL

Некоторое время назад задумался о создании резервных копий баз данных MySQL на своём домашнем «сервере».

Сформировались следующие требования:

  • Бэкапы должны быть полными/инкрементальными, дабы занимать меньше места
  • Бэкапы должны храниться в plain text, чтобы можно было по ним grep'ать и читать их при необходимости


В результате пришёл к идее использования простой связки mysql/diff/patch в одном скрипте, который запускается cron'ом.

Основная хитрость заключается в создании инкрементального бэкапа. Для этого на время «собирается» предыдущая версия базы из полного и всех инкрементальных бэкапов, и с помощью diff создаётся новый инкрементальный бэкап. В остальном алгоритм скрипта тривиален.

Собственно, сам скрипт (/etc/cron.daily/mysql_backup):



#!/bin/bash

PATH=/bin:/sbin:/usr/bin:/usr/sbin

BACKUP_DIR=/wd/backups/mysql
DATE=`date +%F.%H:%M:%S`
# databases to back up
DBS="mysql wikidb gnucash mytinytodo seahub-db seafile-db ccnet-db"
# MySQL credentials
DBU=root
DBP=XXXXXXXXX
# switches for mysqldump
MDUMP_OPTS="--ignore-table=mysql.event --user=${DBU} --password=${DBP} --allow-keywords --comments --skip-dump-date --skip-extended-insert --create-options --quote-names --routines"

################ FUNCTIONS

# create_full() - creates full mysql backup for database $DB
# takes backup file name as argument
function create_full {
  local new_full=$1

  mysqldump ${MDUMP_OPTS} ${DB} > "${new_full}"
}

# create_inc() - creates incremental mysql backup for database $DB
# arguments:
# $1 - new incremental backup file name
# $2 - previous full backup file name
# $3,4,.. - old inc backup files, ordered by creation date, starting from the oldest one
function create_inc {
  local new_inc=$1
  local old_full=$2
  local old_full_fname=${old_full##*/}
  local old_full_patched=/tmp/${old_full_fname}.patched.${DATE}
  local tmp_full=/tmp/${DB}.fulltmp.sql.${DATE}
  local inc

  create_full "${tmp_full}"
  # patch old full with old incs
  cp -f "${old_full}" "${old_full_patched}"
  shift; shift; inc=$1
  while shift; do
    patch --quiet "${old_full_patched}" "${inc}"
    inc=$1
  done
  # create diff
  diff "${old_full_patched}" "${tmp_full}" > "${new_inc}"

  #clean up
  rm -f "$old_full_patched" "$tmp_full"
}

################ MAIN

mkdir -p ${BACKUP_DIR}/tmp
for DB in $DBS; do
  mkdir -p ${BACKUP_DIR}/$DB/{full,diff,inc}
done

if [ $(date +%u) == "1" ]; then         # Full backup each Monday
  for DB in $DBS; do
    new_full=${BACKUP_DIR}/$DB/full/${DB}.full.sql.${DATE}
    create_full "${new_full}"
  done
else                                    # Incremental backup every other day
  for DB in $DBS; do
    previous_full=$(ls -t ${BACKUP_DIR}/$DB/full/${DB}.full.sql.* 2>/dev/null | head -1)
    previous_full_fname=${previous_full##*/}
    previous_incs=$(ls -rt ${BACKUP_DIR}/$DB/inc/${previous_full_fname}.inc.* 2>/dev/null)
    new_full=${BACKUP_DIR}/$DB/full/${DB}.full.sql.${DATE}
    new_inc=${BACKUP_DIR}/$DB/inc/${previous_full_fname}.inc.${DATE}

    if [ -z "$previous_full" ]; then # No previous full backup? Create it then
      create_full $new_full
      echo "$DB new full backup status $?, path $new_full"
    else                             # create new incremental
      create_inc $new_inc $previous_full $previous_incs
      echo "$DB new inc backup status $?, path $new_inc"
    fi
  done
fi

#### Output is sent to administrator's email by cron.
echo "============= Backups ============="
ls -lh ${BACKUP_DIR}/*/{full,inc}/*
echo "============= Size    ============="
du -sxh ${BACKUP_DIR}
du -sxh ${BACKUP_DIR}/*
Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.