Dieses Beispiel wird auf einem Ubuntu 10.04 LTS Server und MySQL 5.1.41-3ubuntu12.1 durchgeführt.
ACHTUNG!
log-slave-updates. Ohne diese Option kann MySQL 5.1 nur einen Master-Slave-Betrieb! Das bedeutet, wenn der Master per BIN-Logs (als quasi als Slave) von einem Master Daten bekommt, dann gibt er sie (in seiner Funktion als Master) nicht weiter. Man sieht es daran, dass die BIN-Log-Position in "SHOW MASTER STATUS" beim einspielen von Daten, nicht weiter hoch läuft. Die BIN-Log-Position in "SHOW SLAVE STATUS" dagegen läuft prima hoch.Zum Beispiel so:
# echo "CHANGE MASTER TO MASTER_HOST='${master_host_name}', MASTER_USER='${replication_user_name}', MASTER_PASSWORD='${replication_password}', MASTER_LOG_FILE='${recorded_log_file_name}', MASTER_LOG_POS=${recorded_log_position};" | mysql -h${slave_host_name} -uroot -p${dbrootpw}
#!/bin/bash
MASTER_HOST="${1}"
BENUTZER="root"
PASSWORT="geheim1"
MASTER_USER="otto"
MASTER_PASSWORD="geheim2"
if [ -n "${MASTER_HOST}" ] ; then
MASTERSTATUS="$(echo "SHOW MASTER STATUS \G;" | mysql -u${BENUTZER} -p${PASSWORT} -h${MASTER_HOST} | awk '/^[ ]*File:/ || /^[ ]*Position:/ {print $NF}')"
BINLOG="$(echo "${MASTERSTATUS}" | head -n1)"
LOGPOS="$(echo "${MASTERSTATUS}" | tail -n1)"
echo "'${MASTER_HOST}', '${BINLOG}', '${LOGPOS}'"
echo "
STOP SLAVE;
CHANGE MASTER TO \
MASTER_HOST='${MASTER_HOST}', \
MASTER_USER='${MASTER_USER}', \
MASTER_PASSWORD='${MASTER_PASSWORD}', \
MASTER_LOG_FILE='${BINLOG}', \
MASTER_LOG_POS=${LOGPOS} \
;
START SLAVE;
" | mysql -u${BENUTZER} -p${PASSWORT}
else
echo "${0} [MySQL-Master]"
fi
# /etc/init.d/mysql stop
# aptitude update && aptitude safe-upgrade && aptitude autoclean || exit 1
# aptitude -y install mysql-server || exit 1
# /etc/init.d/mysql stop
# sed -i 's/^bind-address.*/#&/' /etc/mysql/my.cnf
# rm -fr /var/log/mysql* /var/lib/mysql
# dpkg-reconfigure --force $(dpkg -l | awk '/^ii.+mysql-server-[0-9]/ {print $2}') || exit 1
Alle Dateien, die im Verzeichnis /etc/mysql/conf.d/ liegen und die Endung .cnf haben, werden in die Datei /etc/my.cnf eingebunden:
# ls -l /etc/mysql/conf.d/*.cnf
# echo "GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'geheim' WITH GRANT OPTION;FLUSH PRIVILEGES;" | mysql -uroot -pgeheim
# vi /etc/mysql/conf.d/db_fritz.cnf [mysqld] pid_file = mysql.pid log_bin = mysql-bin #log_bin = /mysqlbinlog/mysql-bin relay_log = mysql-relay-bin relay_log_space_limit = 4G [server] sql-mode=""
Status des Slave:
# echo "SHOW SLAVE STATUS \G;" | mysql -u root -p
Konfigurieren Sie ein Konto auf dem Master-Server, über das der Slave eine Verbindung herstellen kann. Dieses Konto benötigt die Berechtigung REPLICATION SLAVE. Wenn das Konto ausschließlich zur Replikation verwendet wird (was wir empfehlen), dann brauchen Sie keine weiteren Berechtigungen zu gewähren.
Nicht empfohlen dagegen ist soetwas:
# echo "GRANT REPLICATION SLAVE ON *.* TO 'root'@'%' IDENTIFIED BY 'geheim';" | mysql -t -h localhost -u root -p
Besser ist soetwas:
# echo "CREATE USER fritz IDENTIFIED BY 'fritz_geheim';GRANT REPLICATION CLIENT,RELOAD,REPLICATION SLAVE ON *.* TO 'fritz'@'%' IDENTIFIED BY 'fritz_geheim';" | mysql -u root -p
# vi /etc/mysql/conf.d/replication_master.cnf [mysqld] server_id = 1 innodb_flush_log_at_trx_commit=1 sync_binlog = 1 log_slave_updates = 1 expire_logs_days = 3 #master_ssl = #master_ssl_ca = #master_ssl_capath = #master_ssl_cert = #master_ssl_cipher = #master_ssl_key =
Die server_id muss auf jeden MySQL-Rechner eindeutig sein!
Status des Master:
# echo "SHOW MASTER STATUS;" | mysql -t -u root -p File Position Binlog_Do_DB Binlog_Ignore_DB mysql-bin.000003 106
Ob der Slave sich mit dem Master verbunden hat, kann man das so abfragen:
# echo "SHOW SLAVE HOSTS;" | mysql -t -uroot -p Server_id Host Port Rpl_recovery_rank Master_id 20 vdb02 3306 0 10
Slave-Mode auf dem Master deaktivieren:
# echo "STOP SLAVE;CHANGE MASTER TO MASTER_HOST='';"|mysql -t -uroot -p
# vi /etc/mysql/conf.d/replication_slave.cnf [mysqld] server_id = 2 innodb_flush_log_at_trx_commit=1 sync_binlog = 1 log_slave_updates = 1 master_host = db01 master_user = fritz master_password = fritz_geheim master_port = 3306 master_connect_retry = 60 report_host = mysql_slave expire_logs_days = 30 #master_ssl = #master_ssl_ca = #master_ssl_capath = #master_ssl_cert = #master_ssl_cipher = #master_ssl_key =
Die server_id muss auf jeden MySQL-Rechner eindeutig sein!
Damit der MySQL-Server sich nach dem Start nicht mit der falschen Maschine verbindet, muss diese Datei gelöscht werden:
rm /var/lib/mysql/master.info
Log-Position des Master auslesen:
# echo "SHOW MASTER STATUS \G;" | mysql -t -uroot -p -h db01
*************************** 1. row ***************************
File: mysql-bin.000205
Position: 52225615
Binlog_Do_DB:
Binlog_Ignore_DB:
Slave in die richtige Log-Position des Master einhängen:
# echo "STOP SLAVE;CHANGE MASTER TO MASTER_HOST='db01', MASTER_USER='fritz', MASTER_PASSWORD='fritz_geheim', MASTER_LOG_FILE='mysql-bin.000205', MASTER_LOG_POS=52225615;START SLAVE;"|mysql -t -uroot -p -h db02
#!/bin/bash
### Dieses Skript erstellt einen Master-Dump
MASTER_HOST=""
MASTER_USER="root"
MASTER_PASSWD="geheim"
while [ "${#}" -ne "0" ]; do
case "${1}" in
-mh)
MASTER_HOST=${2}
shift
;;
-mu)
MASTER_USER=${2}
shift
;;
-mp)
MASTER_PASSWD=${2}
shift
;;
-h)
echo "
HILFE:
${0} [Option]
-mh [master host]
-mu [master user]
-mp [master passwd]
"
exit 1
;;
*)
if [ "$(echo "${1}"|egrep '^-')" ] ; then
echo "Der Parameter '${1}' wird nicht unterstützt!"
fi
shift
;;
esac
done
#------------------------------------------------------------------------------#
HOSTTEST="${MASTER_HOST}${SLAVE_HOST}"
if [ -z "${HOSTTEST}" ] ; then
echo "
${0} -mh [MASTER_HOST]
${0} -mh [MASTER_HOST] -mu [MASTER_USER] -mp [master_passwd]
zum Beispiel
${0} db01
"
else
ZUFALL="$(head -c 1000 /dev/urandom | tr -cd '[:alnum:]' | cut -b-12)"
### auf dem MASETER
echo "FLUSH TABLES WITH READ LOCK;" | mysql -h${MASTER_HOST} -t -u${MASTER_USER} -p${MASTER_PASSWD}
MASTERSTATUS="$(echo "SHOW MASTER STATUS \G;" | mysql -h${MASTER_HOST} -u ${MASTER_USER} -p${MASTER_PASSWD} | awk '/^[ ]*File:/ || /^[ ]*Position:/ {print $NF}')"
recorded_log_file_name="$(echo "${MASTERSTATUS}"|head -n1)"
recorded_log_position="$(echo "${MASTERSTATUS}"|tail -n1)"
time mysqldump -u${MASTER_USER} -p${MASTER_PASSWD} -h${MASTER_HOST} --opt -Ac --master-data=2 > /tmp/master-dump_${recorded_log_file_name}_${recorded_log_position}_${ZUFALL}.sql
echo "UNLOCK TABLES;" | mysql -u${MASTER_USER} -p${MASTER_PASSWD} -h${MASTER_HOST}
echo "'${MASTERSTATUS}'"
ssh ${MASTER_HOST} 'cat /etc/mysql/debian.cnf' > /tmp/master_debian_${ZUFALL}.cnf
ls -lh /tmp/master-dump_${recorded_log_file_name}_${recorded_log_position}_${ZUFALL}.sql /tmp/master_debian_${ZUFALL}.cnf
#------------------------------------------------------------------------------#
fi
#!/bin/bash
### Dieses Skript spielt den Master-Dump ein und aktiviert die Replikation
MASTER_DUMP=""
SLAVE_HOST=""
SLAVE_USER="root"
SLAVE_PASSWD="geheim"
REPLI_USER="otto"
REPLI_PASSWD="geheim" # FOSp85bLOpzH / Io8OoF1GZuaw3equ
while [ "${#}" -ne "0" ]; do
case "${1}" in
-sh)
SLAVE_HOST=${2}
shift
;;
-su)
SLAVE_USER=${2}
shift
;;
-sp)
SLAVE_PASSWD=${2}
shift
;;
-ru)
REPLI_USER=${2}
shift
;;
-rp)
REPLI_PASSWD=${2}
shift
;;
-ms)
MASTER_DEBIAN=${2} # Debian System-User-Datei vom Master
shift
;;
-md)
MASTER_DUMP=${2} # kompletter Dump vom Master
shift
;;
-h)
echo "
HILFE:
${0} [Option]
-md [master dump Dateiname]
-sh [slave host]
-su [slave user]
-sp [slave passwd]
-ru [repli user]
-rp [repli passwd]
"
exit 1
;;
*)
if [ "$(echo "${1}"|egrep '^-')" ] ; then
echo "Der Parameter '${1}' wird nicht unterstützt!"
fi
shift
;;
esac
done
#------------------------------------------------------------------------------#
HOSTTEST="${MASTER_HOST}${SLAVE_HOST}"
if [ -z "${HOSTTEST}" ] ; then
echo "
${0} -sh [SLAVE_HOST] -md [MASTER_DUMP]
${0} -sh [SLAVE_HOST] -su [SLAVE_USER] -sp [SLAVE_PASSWD] -ru [REPLI_USER] -rp [REPLI_PASSWD] -md [MASTER_DUMP]
zum Beispiel
${0} -sh db02 -ms /tmp/master_debian_KV9n7cIcmKhs.cnf -md /tmp/master-dump_mysql-bin.000205_52225615_KV9n7cIcmKhs.sql
"
else
### auf dem SLAVE
if [ -r "${MASTER_DUMP}" ] ; then
recorded_log_file_name="$(basename ${MASTER_DUMP} | awk -F'_' '{print $2}')"
recorded_log_position="$(basename ${MASTER_DUMP} | awk -F'_' '{print $3}')"
echo "STOP SLAVE;" | mysql -u${SLAVE_USER} -p${SLAVE_PASSWD} -h${SLAVE_HOST}
time mysql -h${SLAVE_HOST} -u${SLAVE_USER} -p${SLAVE_PASSWD} < ${MASTER_DUMP}
echo "CHANGE MASTER TO MASTER_HOST='${MASTER_HOST}', MASTER_USER='${REPLI_USER}', MASTER_PASSWORD='${REPLI_PASSWD}', MASTER_LOG_FILE='${recorded_log_file_name}', MASTER_LOG_POS=${recorded_log_position};" | mysql -h${SLAVE_HOST} -u${SLAVE_USER} -p${SLAVE_PASSWD}
echo "START SLAVE;" | mysql -h${SLAVE_HOST} -u${SLAVE_USER} -p${SLAVE_PASSWD}
if [ -r "${MASTER_DEBIAN}" ] ; then
md-test="$(basename ${MASTER_DUMP} | awk -F'_' '{gsub("[.]"," ");print $4}')"
ms-test="$(basename ${MASTER_DEBIAN} | awk -F'_' '{gsub("[.]"," ");print $3}')"
if [ "${md-test}" == "${ms-test}" ] ; then
cat ${MASTER_DEBIAN} | ssh ${MASTER_HOST} 'cat > /etc/mysql/debian.cnf'
else
echo "${MASTER_DUMP} und ${MASTER_DEBIAN} gehoeren nicht zusammen"
fi
fi
ssh ${SLAVE_HOST} '/etc/init.d/mysql restart'
fi
#------------------------------------------------------------------------------#
fi
Sollte auf dem Slave diese Fehlermeldung erscheinen, dann muss das Passwort aus der Datei /etc/mysql/debian.cnf vom Master auf den Slave übertragen.
# /etc/init.d/mysql start * Starting MariaDB database server mysqld [ OK ] * Checking for corrupt, not cleanly closed and upgrade needing tables. ERROR 1045 (28000): Access denied for user 'debian-sys-maint'@'localhost' (using password: YES)
oder man überträgt einfach die ganze Datei vom Master zum Slave:
# ssh root@db01 'cat /etc/mysql/debian.cnf' | ssh root@db02 'cat > debian.cnf'
In diesem Beispiel lege ich das neue Bin-Log-Verzeichnis in ein LVM.
LVM anlegen:
# fdisk /dev/sda # pvcreate /dev/sda2 # vgcreate system /dev/sda2
LV anlegen und formatieren:
# lvcreate -L 100G -n mysqlbinlog system # mkfs -t ext4 -L mysqlbinlog /dev/system/mysqlbinlog
LV mounten:
# mkdir /mysqlbinlog # echo "LABEL=mysqlbinlog /mysqlbinlog ext4 defaults 0 0" >> /etc/fstab # mount /mysqlbinlog
Apparmor anpassen:
# vi /etc/apparmor.d/usr.sbin.mysqld ... /mysqlbinlog/** rw, }
# /etc/init.d/apparmor restart
MySQL-Konfiguration anpassen, hier muss der Variable log_bin ein absoluter Pfad übergeben werden:
# vi /etc/mysql/conf.d/default.cnf [mysqld] pid_file = /mysqlbinlog/mysql.pid log_bin = /mysqlbinlog/mysql-bin relay_log = /mysqlbinlog/mysql-relay-bin relay_log_space_limit = 4G expire_logs_days = 7 ...
# mkdir /mysqldb # echo "LABEL=mysqldb /mysqldb ext4 defaults 0 0" >> /etc/fstab # mount /mysqldb # mkdir /mysqldb/datadir # chown -R mysql:mysql /mysqldb
MySQL-Konfiguration anpassen, hier muss der Variable log_bin ein absoluter Pfad übergeben werden:
# vi /etc/mysql/conf.d/default.cnf [mysqld] datadir = /mysqldb/datadir ...
Apparmor anpassen:
# vi /etc/apparmor.d/usr.sbin.mysqld ... /mysqldb/** rw, /mysqldb/datadir/** rw, }
# /etc/init.d/apparmor restart
# du -shc /var/lib/mysql/mysql-*bin.* # echo "SHOW BINARY LOGS;" | mysql -t -u root -pgeheim # echo "SHOW MASTER STATUS;" | mysql -t -u root -pgeheim # mysqlbinlog --base64-output=decode-rows --verbose /var/lib/mysql/mysql-bin.000004 > mysql-bin.000004.txt # echo "SHOW BINLOG EVENTS IN 'mysql-bin.000004';" | mysql -t -uroot -pgeheim
Restore der letzten Bin Log Sicherung „mtebosql01.victorvox.net:/var/lib/mysql '/var/lib/mysql'“
# mysqlbinlog --start-position=120 /var/lib/mysql/mysql-bin.000004 | gzip -9 > /var/lib/mysql/recover.sql.gz # zcat /var/lib/mysql/recover.sql.gz | mysql –vvv
#!/bin/bash
DBPASSWD="geheim"
DBMASTER="${1}"
if [ -z "${DBMASTER}" ] ; then
echo "${0} [DB-Master-Host]"
exit 1
fi
DBSLAVES="$(echo "SHOW SLAVE HOSTS \G;" | mysql -uroot -p${DBPASSWD} -h${DBMASTER} | awk '/^[ ]*Host:/ {print $NF}')"
BINLOGNR="$(echo "SHOW MASTER STATUS \G;" | mysql -uroot -p${DBPASSWD} -h${DBMASTER} | awk '/^[ ]*File:/ {gsub("[.]"," ");print $NF}')"
for i in ${DBSLAVES}
do
BINLOGNR="${BINLOGNR}
$(echo "SHOW SLAVE STATUS \G;" | mysql -uroot -p${DBPASSWD} -h${i} | awk '/^[ ]*Master_Log_File:/ {gsub("[.]"," ");print $NF}')"
done
MAXBINLOG="$(echo "${BINLOGNR}" | awk '/^[0-9]/ {print $1}' | sort -n | head -n1)"
BINLOGDEL="PURGE BINARY LOGS TO 'mysql-bin.$(echo "${MAXBINLOG}" | sed 's/.*/000000&/' | rev | cut -c 1-6 | rev)'"
### hier wird tatsaechlich geloescht!!!
echo "${BINLOGDEL};" | mysql -uroot -p${DBPASSWD} -h${DBMASTER}
MySQL-Replikation hat gegenüber einer Lösung mit einer MySQL-DB auf zwei per DRBD gespiegelten Rechnern zwei bedeutende Nachteile:
Aus diesem Grund ist die Replikation einer MySQL-DB nur sinnvoll, wenn man:
ERROR 1794 (HY000) at line 1: Slave is not configured or failed to initialize properly. You must at least set –server-id to enable either a master or a slave. Additional error messages can be found in the MySQL error log.
Wenn es mit diesen Einstellungen zum Fehler "ERROR 1794" kommt:
relay_log = mysql-relay-bin relay_log_info_repository = TABLE relay_log_recovery = 1 sync_relay_log_info = 1 relay_log_space_limit = 4G
… dann muss man nur einen Parameter entfernen:
#relay_log = mysql-relay-bin relay_log_info_repository = TABLE relay_log_recovery = 1 sync_relay_log_info = 1 relay_log_space_limit = 4G
… und schon geht es nach dem Restart.
#!/bin/bash if [ -z "${1}" ] ; then echo "${0} /var/lib/mysql/mysql-bin.002036"; echo "less /var/lib/mysql/mysql-bin.002036.txt"; echo echo "${0} /var/lib/mysql/mysql-bin.002036 107"; echo "less /var/lib/mysql/mysql-bin.002036_107.txt"; exit 1; else if [ -r "${1}" ] ; then MASTER_LOG_FILE="${1}" BIN_LOG_OUT="${1}" if [ -n "${2}" ] ; then READ_MASTER_LOG_POS="${2}" MASTER_LOG_POSITION="-j ${READ_MASTER_LOG_POS}" BIN_LOG_OUT="${1}_${2}" fi else echo "Die BIN-LOG-Datei konnte nicht gelesen bzw. gefunden werden."; exit 1; fi fi # mysqlbinlog --base64-output=DECODE-ROWS -v -j 1013 /var/lib/mysql/mysql-bin.002035 > /tmp/mysql-bin.txt mysqlbinlog --base64-output=DECODE-ROWS -v ${MASTER_LOG_POSITION} ${MASTER_LOG_FILE} > ${BIN_LOG_OUT}.txt ls -l ${BIN_LOG_OUT}.txt
#!/bin/bash echo " STOP SLAVE; SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1; START SLAVE; SHOW SLAVE STATUS \G; " | mysql