MySQL Backup Strategien: LVM-Snapshots

Master  – Slave mit LVM

Vorteile

  • Restorezeit ist wesentlich geringer bei großen Datenbanken und vielen Indexen (abhängig von Lese- und Netzwerggeschwindigkeit).
  • Geringe Ausfallzeit des Servers – keine Ausfallzeit des Arbeitsbetrieb
  • Der Master kann unbeeinträchtigt seine Arbeit vortsetzen.

Nachteile

  • „Eben mal“ eine Tabelle oder Daten zurückspielen geht nicht, hierzu müssen wir ein kompletten Restore auf ein weiteres System durchführen, um dort mittels mysqldump die Daten zu extrahieren.

Szenario:

Wir benötigen mindestens 2 MySQL Instanzen die miteinander replizieren, damit verringern wir die Ausfallzeit auf 0. Also jedes beliebige Master – Slave oder Master – Master Szenaria, wobei wir am besten einen Slave für das Backup einrichten. Die Slave Instanz kann sich durchaus, wenn es Plattenplatz, CPU, RAM sowie die IO Werte zulassen, auf dem selben Host befinden. Eine Anleitung wie man eine Replikation aufsetzt findet man hier. Natürlich funktioniert das Ganze auch ohne Replikation, allerdings hat man dann, je nach Datenbank, eine Ausfallzeit von 10s – x Minuten. Wichtig, die my.cnf muss so angepasst werden, dass sich die Daten auf einem Logical Volumen (LVM) befinden. Darüber hinaus benötigen wir in der Volumengroup genügend Restplatz, damit uns der Snapshot nicht während des Backups vollläuft. Hier gibt es keine Faustformel das muss man einfach ausrechnen oder probieren.

In meinen Augen hat es sich relativ nützlich erwiesen die Konfiguration der MySQL Instanz wie folgt aufzugliedern:

  1. Das LV wird in ein Verzeichnis gemountet, beispielsweise /data.
  2. Unterhalb dieses Verzeichnisses werden unterschiedliche Verzeichnisse für binlogs, relaylogs, tmp, data sowie error und slowlog angelegt.

Das vereinfacht das Sichern insofern, dass wir hier nur bestimmte Verzeichnisse wegsichern müssen. Es ist auch keine schlechte Idee auf dem Slave das Binlog zu aktivieren und diese ebenfalls zu sichern (z.B. stündlich), damit wir ein sogenanntes Point in Time Recovery machen können (wird an einem späteren Zeitpunkt abgehandelt). Unsere my.cnf sieht dann an den entsprechenden Punkten wie folgt aus:

relay-log                      = /data/mysql-relaylog/relay.log
relay-log-info-file            = /data/mysql-relaylog/relay-log.info
relay-log-index                = /data/mysql-relaylog/relay-log.index
log_error                      = /data/mysql-log/mysql.err
log-slow-queries               = /data/mysql-log/slow_queries.log
log_bin                        = /data/mysql-binlog/mysql-bin.log
innodb_log_group_home_dir      = /data/mysql-data
innodb_log_arch_dir            = /data/mysql-data
innodb_data_home_dir           = /data/mysql-data
datadir                        = /data/mysql-data

Für den Fall, dass wir einen weiteren Slave oder Master wieder herstellen müssen, benötigen wir noch die Postion und das Binlogfile, damit wir die Replikation an diesem Punkt wieder aufsetzen können. Im Prinzip gehen wir im weiteren wie folgt vor: wir stoppen den MySQL Dienst, dann ermitteln wir Binlog File und Position des Masters, legen einen Snapshot an, starten den MySQL Dienst und dann können wir das Backup vom Snapshot starten. Das Vorabscript für den Backup Dienst könnte wie folgt aussehen – bitte beachten, die Pfade und Variablen an die eigene Umgebung anpassen (Volumen und Volumengroupbezeichnungen zum Beispiel).

#!/bin/sh
# Groesse des Snapshots
GROESSE=20G

# Name des Snapshot
NAME=mysql-backup

# Geraetedateiname
# hier das LVS anpassen
GERAET=/dev/db/data

# Mount-Punkt
MOUNT=/backup

# Spiegelgeraet
SPIEGEL=/dev/db/mysql-backup
# Stoppe Datenbank
echo
echo "Shutting down lively database  `date`"
echo
/etc/init.d/mysql stop

#gegebenen Fall diesen Wert anpassen, wenn die Datenbank länger braucht
sleep 3

# Erzeuge Restore Daten für Master - Master
name=$(tail -1 /data/mysql-binlog/mysql-bin.index)
size=$(stat -c '%s' ${name})
echo "binlog Position $name $size" > /data/mysql-data/db_restore.info

# Erzeuge Spiegel
lvcreate --size $GROESSE --snapshot --name $NAME $GERAET
ERGEBNIS1=`echo $?`

if [ $ERGEBNIS1 -ne 0 ]; then
echo
echo
echo "  -- F E H L E R -- "
echo
echo "  Starte Datenbank ohne Backup `date`"
echo
echo
/etc/init.d/mysql start
exit 22
fi

sleep 3

# starte Mysql wieder

echo
echo "Restarting lively database  `date`"
echo
/etc/init.d/mysql start

# pruefe auf mount und mounte Spiegel oder exit

mount | grep /backup
ERGEBNIS2=`echo $?`

if [ $ERGEBNIS2 -eq 0 ]; then
echo
echo
echo "  -- F E H L E R -- "
echo
echo "   Der Mount besteht bereits"
echo
echo
exit 23
else
mount -o ro $SPIEGEL $MOUNT
fi

exit 0

Das Resultat an dieser Stelle sollte ein Read only eingemounteter Snapshot unter /backup sein. Dem Backup Programm weisen wir jetzt nur noch an, die Verzeichnisse:

/backup/mysql-data
/backup/mysql-relaylog

zu sichern.