MySQL mit Replikation

Dieses Beispiel wird auf einem Ubuntu 10.04 LTS Server und MySQL 5.1.41-3ubuntu12.1 durchgeführt.

ACHTUNG!

  1. Die server_id muss immer eindeutig sein!
  2. In aktuellen Versionen von MySQL und MariaDB wird das Passwort für den Replikations-User nicht mehr aus einer CNF-Datei ausgelesen, sondern muss per SQL-Aufruf eingetütet werden.
  3. z.Z. (MySQL 5.1) braucht MySQL für einen Master-Master-Betrieb die Option 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}
Master und Slave jetzt verbinden
#!/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

DB-Host neu initialisieren

# /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

Replikation einrichten

  1. 172.16.0.1: Master (db01)
  2. 172.16.0.2: Slave (db02)

my.cnf

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

Master + Slave

# 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
[server]
sql-mode=""

Status des Slave:

# echo "SHOW SLAVE STATUS \G;" | mysql -u root -p

db01 (Master)

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 SUPER,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

db02 (Slave)

# 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!

Master und Slave verbinden

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
db_master-dump.sh
#!/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 -A --master-data > /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
db_master-slave_repli_on.sh
#!/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
Access denied for user 'debian-sys-maint'@'localhost'

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'

Probleme

MySQL-Replikation hat gegenüber einer Lösung mit einer MySQL-DB auf zwei per DRBD gespiegelten Rechnern zwei bedeutende Nachteile:

  1. Ein automatischer Schwenk zwischen den beiden MySQL-DB's in Replikation, stellte sich (in der Praxis) als nicht so einfach heraus, wie mit DRBD.
  2. Wenn beim Schwenk auch nur ein einziges SQL-Kommando auf dem Slave nicht ausgeführt wird, sind (unter Umständen) die beiden DB's nicht mehr (automatisch) synchron zu bekommen. Dann steht zwar die Verbindung zwischen Master und Slave, aber der Slave snchronisiert sich nicht mehr…

Aus diesem Grund ist die Replikation einer MySQL-DB nur sinnvoll, wenn man:

  1. eine oder mehrere DBD's als „Sicherheitskopien“ haben möchte, die im Fehlerfall nicht einspringen müssen
  2. viele Lesezugriffe auf mehrere MySQL-Systeme verteilen möchte
 
Falls nicht anders bezeichnet, ist der Inhalt dieses Wikis unter der folgenden Lizenz veröffentlicht:GNU Free Documentation License 1.2
Recent changes RSS feed Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki