Wie wir bereits gesehen haben, kann es für Unternehmen schwierig sein, ihre Daten aus RDS for MySQL zu verschieben. Im ersten Teil dieses Blogs haben wir Ihnen gezeigt, wie Sie Ihre Zielumgebung auf EC2 einrichten und eine Proxy-Schicht (ProxySQL) zwischen Ihren Anwendungen und RDS einfügen. In diesem zweiten Teil zeigen wir Ihnen, wie Sie die eigentliche Datenmigration auf Ihren eigenen Server durchführen und Ihre Anwendungen dann ohne Ausfallzeiten auf die neue Datenbankinstanz umleiten.
Kopieren von Daten aus RDS
Sobald unser Datenbankverkehr über ProxySQL läuft, können wir mit den Vorbereitungen beginnen, um unsere Daten aus RDS zu kopieren. Wir müssen dies tun, um die Replikation zwischen RDS und unserer auf EC2 ausgeführten MySQL-Instanz einzurichten. Sobald dies erledigt ist, konfigurieren wir ProxySQL so, dass der Datenverkehr von RDS zu unserem MySQL/EC2 umgeleitet wird.
Wie wir im ersten Blogbeitrag dieser Serie besprochen haben, können Sie Daten nur über einen logischen Dump aus dem RDS abrufen. Ohne Zugriff auf die Instanz können wir keine heißen, physischen Backup-Tools wie xtrabackup verwenden. Wir können auch keine Snapshots verwenden, da es keine andere Möglichkeit gibt, als eine neue RDS-Instanz aus dem Snapshot zu erstellen.
Wir sind auf logische Dump-Tools beschränkt, daher wäre die logische Option, mydumper/myloader zu verwenden, um die Daten zu verarbeiten. Glücklicherweise kann mydumper konsistente Backups erstellen, sodass wir uns darauf verlassen können, dass es Binlog-Koordinaten bereitstellt, mit denen sich unser neuer Slave verbinden kann. Das Hauptproblem beim Erstellen von RDS-Replikaten ist die Binlog-Rotationsrichtlinie – das logische Sichern und Laden kann bei größeren Datensätzen (Hunderte von Gigabyte) sogar Tage dauern und Sie müssen für die Dauer dieses gesamten Prozesses Binlogs auf der RDS-Instanz aufbewahren. Sicher, Sie können die Aufbewahrung der Binlog-Rotation auf RDS erhöhen (rufen Sie mysql.rds_set_configuration('binlog-Aufbewahrungsstunden', 24); - Sie können sie bis zu 7 Tage aufbewahren), aber es ist viel sicherer, es anders zu machen.
Bevor wir mit dem Erstellen eines Speicherauszugs fortfahren, fügen wir unserer RDS-Instanz ein Replikat hinzu.
Amazon RDS-Dashboard Replikat-DB in RDS erstellenSobald wir auf die Schaltfläche „Create Read Replica“ klicken, wird ein Snapshot auf der „Master“-RDS-Replik gestartet. Es wird verwendet, um den neuen Slave bereitzustellen. Der Vorgang kann Stunden dauern, es hängt alles von der Größe des Volumes ab, wann wurde das letzte Mal ein Snapshot erstellt und der Leistung des Volumes (io1/gp2? Magnetisch? Wie viele pIOPS hat ein Volume?).
Master-RDS-ReplikWenn der Slave bereit ist (sein Status hat sich auf „verfügbar“ geändert), können wir uns über seinen RDS-Endpunkt anmelden.
RDS-SlaveSobald wir eingeloggt sind, stoppen wir die Replikation auf unserem Slave – dies stellt sicher, dass der RDS-Master Binärprotokolle nicht löscht und sie für unseren EC2-Slave immer noch verfügbar sind, sobald wir unseren Dump/Reload-Prozess abgeschlossen haben.
mysql> CALL mysql.rds_stop_replication;
+---------------------------+
| Message |
+---------------------------+
| Slave is down or disabled |
+---------------------------+
1 row in set (1.02 sec)
Query OK, 0 rows affected (1.02 sec)
Jetzt ist es endlich an der Zeit, Daten auf EC2 zu kopieren. Zuerst müssen wir mydumper installieren. Sie können es von github bekommen:https://github.com/maxbube/mydumper. Der Installationsprozess ist ziemlich einfach und in der Readme-Datei gut beschrieben, daher werden wir ihn hier nicht behandeln. Höchstwahrscheinlich müssen Sie ein paar Pakete installieren (aufgelistet in der Readme) und der schwierigere Teil ist zu identifizieren, welches Paket mysql_config enthält – es hängt von der MySQL-Variante ab (und manchmal auch von der MySQL-Version).
Sobald Sie mydumper kompiliert und einsatzbereit haben, können Sie es ausführen:
[email protected]:~/mydumper# mkdir /tmp/rdsdump
[email protected]:~/mydumper# ./mydumper -h rds2.cvsw8xpajw2b.us-east-1.rds.amazonaws.com -p tpccpass -u tpcc -o /tmp/rdsdump --lock-all-tables --chunk-filesize 100 --events --routines --triggers
.
Bitte beachten Sie --lock-all-tables, das sicherstellt, dass der Snapshot der Daten konsistent ist und es möglich ist, ihn zum Erstellen eines Slaves zu verwenden. Jetzt müssen wir warten, bis mydumper seine Aufgabe erledigt hat.
Ein weiterer Schritt ist erforderlich – wir möchten das MySQL-Schema nicht wiederherstellen, aber wir müssen Benutzer und ihre Berechtigungen kopieren. Dafür können wir pt-show-grants verwenden:
[email protected]:~# wget http://percona.com/get/pt-show-grants
[email protected]:~# chmod u+x ./pt-show-grants
[email protected]:~# ./pt-show-grants -h rds2.cvsw8xpajw2b.us-east-1.rds.amazonaws.com -u tpcc -p tpccpass > grants.sql
Beispiel für pt-show-grants könnte so aussehen:
-- Grants for 'sbtest'@'%'
CREATE USER IF NOT EXISTS 'sbtest'@'%';
ALTER USER 'sbtest'@'%' IDENTIFIED WITH 'mysql_native_password' AS '*2AFD99E79E4AA23DE141540F4179F64FFB3AC521' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
GRANT ALTER, ALTER ROUTINE, CREATE, CREATE ROUTINE, CREATE TEMPORARY TABLES, CREATE USER, CREATE VIEW, DELETE, DROP, EVENT, EXECUTE, INDEX, INSERT, LOCK TABLES, PROCESS, REFERENCES, RELOAD, REPLICATION CLIENT, REPLICATION SLAVE, SELECT, SHOW DATABASES, SHOW VIEW, TRIGGER, UPDATE ON *.* TO 'sbtest'@'%';
Es liegt an Ihnen auszuwählen, welche Benutzer auf Ihre MySQL/EC2-Instanz kopiert werden müssen. Es macht keinen Sinn, es für alle zu tun. Zum Beispiel haben Root-Benutzer kein „SUPER“-Privileg auf RDS, also ist es besser, sie von Grund auf neu zu erstellen. Was Sie kopieren müssen, sind Berechtigungen für Ihren Anwendungsbenutzer. Wir müssen auch Benutzer kopieren, die von ProxySQL (in unserem Fall Proxysql-Monitor) verwendet werden.
Einfügen von Daten in Ihre MySQL/EC2-Instanz
Wie oben erwähnt, möchten wir keine Systemschemas wiederherstellen. Daher werden wir Dateien, die sich auf diese Schemas beziehen, aus unserem mydumper-Verzeichnis verschieben:
[email protected]:~# mkdir /tmp/rdsdump_sys/
[email protected]:~# mv /tmp/rdsdump/mysql* /tmp/rdsdump_sys/
[email protected]:~# mv /tmp/rdsdump/sys* /tmp/rdsdump_sys/
Wenn wir damit fertig sind, ist es an der Zeit, Daten in die MySQL/EC2-Instanz zu laden:
[email protected]:~/mydumper# ./myloader -d /tmp/rdsdump/ -u tpcc -p tpccpass -t 4 --overwrite-tables -h 172.30.4.238
Bitte beachten Sie, dass wir vier Threads (-t 4) verwendet haben – stellen Sie sicher, dass Sie dies so einstellen, wie es in Ihrer Umgebung sinnvoll ist. Es geht darum, die MySQL-Zielinstanz zu sättigen – entweder CPU oder E/A, je nach Engpass. Wir wollen so viel wie möglich herausholen, um sicherzustellen, dass wir alle verfügbaren Ressourcen zum Laden der Daten verwendet haben.
Nachdem die Hauptdaten geladen wurden, müssen zwei weitere Schritte unternommen werden, beide beziehen sich auf RDS-Interna und beide können unsere Replikation unterbrechen. Erstens enthält RDS ein paar rds_*-Tabellen im mysql-Schema. Wir wollen sie laden, falls einige von ihnen von RDS verwendet werden – die Replikation bricht ab, wenn unser Slave sie nicht hat. Wir können es auf folgende Weise tun:
[email protected]:~/mydumper# for i in $(ls -alh /tmp/rdsdump_sys/ | grep rds | awk '{print $9}') ; do echo $i ; mysql -ppass -uroot mysql < /tmp/rdsdump_sys/$i ; done
mysql.rds_configuration-schema.sql
mysql.rds_configuration.sql
mysql.rds_global_status_history_old-schema.sql
mysql.rds_global_status_history-schema.sql
mysql.rds_heartbeat2-schema.sql
mysql.rds_heartbeat2.sql
mysql.rds_history-schema.sql
mysql.rds_history.sql
mysql.rds_replication_status-schema.sql
mysql.rds_replication_status.sql
mysql.rds_sysinfo-schema.sql
Ein ähnliches Problem besteht bei Zeitzonentabellen, wir müssen sie mit Daten aus der RDS-Instanz laden:
[email protected]:~/mydumper# for i in $(ls -alh /tmp/rdsdump_sys/ | grep time_zone | grep -v schema | awk '{print $9}') ; do echo $i ; mysql -ppass -uroot mysql < /tmp/rdsdump_sys/$i ; done
mysql.time_zone_name.sql
mysql.time_zone.sql
mysql.time_zone_transition.sql
mysql.time_zone_transition_type.sql
Wenn das alles fertig ist, können wir die Replikation zwischen RDS (Master) und unserer MySQL/EC2-Instanz (Slave) einrichten.
Replikation einrichten
Mydumper schreibt bei der Durchführung eines konsistenten Dumps eine Binärlog-Position auf. Wir finden diese Daten in einer Datei namens Metadata im Dump-Verzeichnis. Werfen wir einen Blick darauf, wir werden dann die Position verwenden, um die Replikation einzurichten.
[email protected]:~/mydumper# cat /tmp/rdsdump/metadata
Started dump at: 2017-02-03 16:17:29
SHOW SLAVE STATUS:
Host: 10.1.4.180
Log: mysql-bin-changelog.007079
Pos: 10537102
GTID:
Finished dump at: 2017-02-03 16:44:46
Eine letzte Sache, die uns fehlt, ist ein Benutzer, mit dem wir unseren Sklaven einrichten könnten. Lassen Sie uns eine auf der RDS-Instanz erstellen:
[email protected]:~# mysql -ppassword -h rds2.cvsw8xpajw2b.us-east-1.rds.amazonaws.com
mysql> CREATE USER IF NOT EXISTS 'rds_rpl'@'%' IDENTIFIED BY 'rds_rpl_pass';
Query OK, 0 rows affected (0.04 sec)
mysql> GRANT REPLICATION SLAVE ON *.* TO 'rds_rpl'@'%';
Query OK, 0 rows affected (0.01 sec)
Jetzt ist es an der Zeit, unseren MySQL/EC2-Server von der RDS-Instanz abzukoppeln:
mysql> CHANGE MASTER TO MASTER_HOST='rds2.cvsw8xpajw2b.us-east-1.rds.amazonaws.com', MASTER_USER='rds_rpl', MASTER_PASSWORD='rds_rpl_pass', MASTER_LOG_FILE='mysql-bin-changelog.007079', MASTER_LOG_POS=10537102;
Query OK, 0 rows affected, 2 warnings (0.03 sec)
mysql> START SLAVE;
Query OK, 0 rows affected (0.02 sec)
mysql> SHOW SLAVE STATUS\G
*************************** 1. row ***************************
Slave_IO_State: Queueing master event to the relay log
Master_Host: rds2.cvsw8xpajw2b.us-east-1.rds.amazonaws.com
Master_User: rds_rpl
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin-changelog.007080
Read_Master_Log_Pos: 13842678
Relay_Log_File: relay-bin.000002
Relay_Log_Pos: 20448
Relay_Master_Log_File: mysql-bin-changelog.007079
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 10557220
Relay_Log_Space: 29071382
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: 258726
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 1237547456
Master_UUID: b5337d20-d815-11e6-abf1-120217bb3ac2
Master_Info_File: mysql.slave_master_info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: System lock
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set:
Executed_Gtid_Set:
Auto_Position: 0
Replicate_Rewrite_DB:
Channel_Name:
Master_TLS_Version:
1 row in set (0.01 sec)
Der letzte Schritt besteht darin, unseren Datenverkehr von der RDS-Instanz auf MySQL/EC2 umzustellen, aber wir müssen ihn zuerst aufholen.
Wenn der Sklave aufgeholt hat, müssen wir einen Cutover durchführen. Um dies zu automatisieren, haben wir uns entschieden, ein kurzes Bash-Skript vorzubereiten, das sich mit ProxySQL verbindet und das tut, was getan werden muss.
# At first, we define old and new masters
OldMaster=rds2.cvsw8xpajw2b.us-east-1.rds.amazonaws.com
NewMaster=172.30.4.238
(
# We remove entries from mysql_replication_hostgroup so ProxySQL logic won’t interfere
# with our script
echo "DELETE FROM mysql_replication_hostgroups;"
# Then we set current master to OFFLINE_SOFT - this will allow current transactions to
# complete while not accepting any more transactions - they will wait (by default for
# 10 seconds) for a master to become available again.
echo "UPDATE mysql_servers SET STATUS='OFFLINE_SOFT' WHERE hostname=\"$OldMaster\";"
echo "LOAD MYSQL SERVERS TO RUNTIME;"
) | mysql -u admin -padmin -h 127.0.0.1 -P6032
# Here we are going to check for connections in the pool which are still used by
# transactions which haven’t closed so far. If we see that neither hostgroup 10 nor
# hostgroup 20 has open transactions, we can perform a switchover.
CONNUSED=`mysql -h 127.0.0.1 -P6032 -uadmin -padmin -e 'SELECT IFNULL(SUM(ConnUsed),0) FROM stats_mysql_connection_pool WHERE status="OFFLINE_SOFT" AND (hostgroup=10 OR hostgroup=20)' -B -N 2> /dev/null`
TRIES=0
while [ $CONNUSED -ne 0 -a $TRIES -ne 20 ]
do
CONNUSED=`mysql -h 127.0.0.1 -P6032 -uadmin -padmin -e 'SELECT IFNULL(SUM(ConnUsed),0) FROM stats_mysql_connection_pool WHERE status="OFFLINE_SOFT" AND (hostgroup=10 OR hostgroup=20)' -B -N 2> /dev/null`
TRIES=$(($TRIES+1))
if [ $CONNUSED -ne "0" ]; then
sleep 0.05
fi
done
# Here is our switchover logic - we basically exchange hostgroups for RDS and EC2
# instance. We also configure back mysql_replication_hostgroups table.
(
echo "UPDATE mysql_servers SET STATUS='ONLINE', hostgroup_id=110 WHERE hostname=\"$OldMaster\" AND hostgroup_id=10;"
echo "UPDATE mysql_servers SET STATUS='ONLINE', hostgroup_id=120 WHERE hostname=\"$OldMaster\" AND hostgroup_id=20;"
echo "UPDATE mysql_servers SET hostgroup_id=10 WHERE hostname=\"$NewMaster\" AND hostgroup_id=110;"
echo "UPDATE mysql_servers SET hostgroup_id=20 WHERE hostname=\"$NewMaster\" AND hostgroup_id=120;"
echo "INSERT INTO mysql_replication_hostgroups VALUES (10, 20, 'hostgroups');"
echo "LOAD MYSQL SERVERS TO RUNTIME;"
) | mysql -u admin -padmin -h 127.0.0.1 -P6032
Wenn alles fertig ist, sollten Sie den folgenden Inhalt in der mysql_servers-Tabelle sehen:
mysql> select * from mysql_servers;
+--------------+-----------------------------------------------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+-------------+
| hostgroup_id | hostname | port | status | weight | compression | max_connections | max_replication_lag | use_ssl | max_latency_ms | comment |
+--------------+-----------------------------------------------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+-------------+
| 20 | 172.30.4.238 | 3306 | ONLINE | 1 | 0 | 100 | 10 | 0 | 0 | read server |
| 10 | 172.30.4.238 | 3306 | ONLINE | 1 | 0 | 100 | 10 | 0 | 0 | read server |
| 120 | rds2.cvsw8xpajw2b.us-east-1.rds.amazonaws.com | 3306 | ONLINE | 1 | 0 | 100 | 10 | 0 | 0 | |
| 110 | rds2.cvsw8xpajw2b.us-east-1.rds.amazonaws.com | 3306 | ONLINE | 1 | 0 | 100 | 10 | 0 | 0 | |
+--------------+-----------------------------------------------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+-------------+
Auf der Anwendungsseite sollten Sie dank der Fähigkeit von ProxySQL, Abfragen für einige Zeit in eine Warteschlange zu stellen, keine großen Auswirkungen sehen.
Damit haben wir den Umzug Ihrer Datenbank von RDS nach EC2 abgeschlossen. Als letzten Schritt müssen wir unseren RDS-Slave entfernen - er hat seinen Zweck erfüllt und kann gelöscht werden.
In unserem nächsten Blogbeitrag bauen wir darauf auf. Wir werden ein Szenario durchgehen, in dem wir unsere Datenbank von AWS/EC2 zu einem separaten Hosting-Anbieter verschieben.