Mysql
 sql >> Baza danych >  >> RDS >> Mysql

MySQL w chmurze — migracja online z Amazon RDS na własny serwer:część 2

Jak widzieliśmy wcześniej, dla firm może być trudne przeniesienie danych z RDS dla MySQL. W pierwszej części tego bloga pokazaliśmy, jak skonfigurować środowisko docelowe na EC2 i wstawić warstwę proxy (ProxySQL) między aplikacjami a RDS. W tej drugiej części pokażemy, jak przeprowadzić rzeczywistą migrację danych na własny serwer, a następnie przekierować aplikacje do nowej instancji bazy danych bez przestojów.

Kopiowanie danych z RDS

Gdy już ruch naszej bazy danych będzie przebiegał przez ProxySQL, możemy rozpocząć przygotowania do skopiowania naszych danych z RDS. Musimy to zrobić, aby skonfigurować replikację między RDS a naszą instancją MySQL działającą na EC2. Po wykonaniu tej czynności skonfigurujemy ProxySQL, aby przekierowywał ruch z RDS do naszego MySQL/EC2.

Jak omówiliśmy w pierwszym poście na blogu z tej serii, jedynym sposobem na uzyskanie danych z RDS jest logiczny zrzut. Bez dostępu do instancji nie możemy używać żadnych gorących, fizycznych narzędzi do tworzenia kopii zapasowych, takich jak xtrabackup. Nie możemy również używać migawek, ponieważ nie ma sposobu na zbudowanie niczego innego niż nowa instancja RDS ze zrzutu.

Jesteśmy ograniczeni do narzędzi do zrzutów logicznych, dlatego logiczną opcją byłoby użycie mydumper/myloader do przetwarzania danych. Na szczęście mydumper może tworzyć spójne kopie zapasowe, dzięki czemu możemy na nim polegać, aby zapewnić współrzędne binlogu dla naszego nowego niewolnika, z którym może się połączyć. Głównym problemem podczas tworzenia replik RDS jest polityka rotacji logów binlogów — logiczny zrzut i ładowanie może zająć nawet kilka dni w przypadku większych (setek gigabajtów) zestawów danych i musisz przechowywać binlogi na instancji RDS przez cały czas trwania tego procesu. Jasne, możesz zwiększyć retencję rotacji binlogów w RDS (zadzwoń do mysql.rds_set_configuration('godziny przechowywania binlog', 24); - możesz zachować je do 7 dni), ale dużo bezpieczniej jest zrobić to inaczej.

Zanim przystąpimy do robienia zrzutu, dodamy replikę do naszej instancji RDS.

Pulpit nawigacyjny Amazon RDS Utwórz bazę danych repliki w RDS

Po kliknięciu przycisku „Utwórz replikę do odczytu” na „głównej” replice RDS zostanie uruchomiona migawka. Będzie używany do zaopatrzenia nowego niewolnika. Proces może zająć kilka godzin, wszystko zależy od rozmiaru wolumenu, kiedy ostatnio zrobiono migawkę i wydajności wolumenu (io1/gp2? Magnetyczny? Ile pIOPS ma wolumen?).

Replika główna RDS

Gdy urządzenie podrzędne jest gotowe (jego status zmienił się na „dostępny”), możemy się do niego zalogować za pomocą jego punktu końcowego RDS.

Podrzędny system RDS

Po zalogowaniu zatrzymamy replikację na naszym urządzeniu podrzędnym – zapewni to, że urządzenie główne RDS nie wyczyści dzienników binarnych i będą one nadal dostępne dla naszego urządzenia podrzędnego EC2 po zakończeniu procesu zrzutu/przeładowania.

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)

Teraz w końcu nadszedł czas na skopiowanie danych do EC2. Najpierw musimy zainstalować mydumper. Możesz go pobrać z github:https://github.com/maxbube/mydumper. Proces instalacji jest dość prosty i ładnie opisany w pliku readme, więc nie będziemy go tutaj omawiać. Najprawdopodobniej będziesz musiał zainstalować kilka pakietów (wymienionych w pliku readme), a trudniejsze jest zidentyfikowanie, który pakiet zawiera mysql_config - zależy to od smaku MySQL (a czasami także wersji MySQL).

Po skompilowaniu mydumper i gotowości do pracy możesz go uruchomić:

[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
. 

Proszę zwrócić uwagę na --lock-all-tables, który zapewnia, że ​​zrzut danych będzie spójny i będzie można go użyć do stworzenia slave'a. Teraz musimy poczekać, aż mydumper wykona swoje zadanie.

Wymagany jest jeszcze jeden krok - nie chcemy przywracać schematu mysql, ale musimy skopiować użytkowników i ich granty. Możemy do tego użyć pt-show-grants:

[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

Przykładowe granty pt-show mogą wyglądać tak:

-- 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'@'%';

Od Ciebie zależy, jakich użytkowników należy skopiować do Twojej instancji MySQL/EC2. Nie ma sensu robić tego za wszystkich. Na przykład użytkownicy root nie mają uprawnień „SUPER” w RDS, więc lepiej jest odtworzyć ich od zera. To, co musisz skopiować, to dotacje dla użytkownika aplikacji. Musimy również skopiować użytkowników używanych przez ProxySQL (w naszym przypadku proxysql-monitor).

Wstawianie danych do Twojej instancji MySQL/EC2

Jak wspomniano powyżej, nie chcemy przywracać schematów systemowych. Dlatego przeniesiemy pliki związane z tymi schematami z naszego katalogu mydumper:

[email protected]:~# mkdir /tmp/rdsdump_sys/
[email protected]:~# mv /tmp/rdsdump/mysql* /tmp/rdsdump_sys/
[email protected]:~# mv /tmp/rdsdump/sys* /tmp/rdsdump_sys/

Kiedy skończymy, czas zacząć ładować dane do instancji MySQL/EC2:

[email protected]:~/mydumper# ./myloader -d /tmp/rdsdump/ -u tpcc -p tpccpass -t 4 --overwrite-tables -h 172.30.4.238

Pamiętaj, że użyliśmy czterech wątków (-t 4) - upewnij się, że ustawiłeś to, co ma sens w twoim środowisku. Wszystko sprowadza się do nasycenia docelowej instancji MySQL — albo procesora, albo I/O, w zależności od wąskiego gardła. Chcemy wycisnąć z niego jak najwięcej, aby mieć pewność, że wykorzystaliśmy wszystkie dostępne zasoby do załadowania danych.

Po załadowaniu głównych danych należy wykonać jeszcze dwa kroki, oba są związane z wewnętrznymi elementami RDS i oba mogą przerwać naszą replikację. Po pierwsze, RDS zawiera kilka tabel rds_* w schemacie mysql. Chcemy je załadować na wypadek, gdyby niektóre z nich były używane przez RDS - replikacja zepsuje się, jeśli nasz slave ich nie będzie miał. Możemy to zrobić w następujący sposób:

[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

Podobny problem jest z tabelami stref czasowych, musimy je załadować za pomocą danych z instancji RDS:

[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

Gdy wszystko jest gotowe, możemy skonfigurować replikację między RDS (master) a naszą instancją MySQL/EC2 (slave).

Konfigurowanie replikacji

Mydumper podczas wykonywania spójnego zrzutu zapisuje pozycję dziennika binarnego. Możemy znaleźć te dane w pliku o nazwie metadata w katalogu zrzutu. Rzućmy okiem na to, następnie użyjemy tej pozycji do skonfigurowania replikacji.

[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

Ostatnią rzeczą, której nam brakuje, jest użytkownik, którego moglibyśmy użyć do skonfigurowania naszego niewolnika. Utwórzmy jeden na instancji RDS:

[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)

Teraz nadszedł czas, aby podporządkować nasz serwer MySQL/EC2 od instancji RDS:

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)

Ostatnim krokiem będzie przełączenie naszego ruchu z instancji RDS na MySQL/EC2, ale najpierw musimy pozwolić mu nadrobić zaległości.

Kiedy niewolnik dogoni, musimy dokonać przełączenia. Aby to zautomatyzować, postanowiliśmy przygotować krótki skrypt basha, który połączy się z ProxySQL i zrobi to, co należy zrobić.

# 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

Kiedy wszystko jest zrobione, powinieneś zobaczyć następującą zawartość w tabeli mysql_servers:

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              |             |
+--------------+-----------------------------------------------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+-------------+

Po stronie aplikacji nie powinieneś widzieć większego wpływu, dzięki zdolności ProxySQL do kolejkowania zapytań przez pewien czas.

Tym samym zakończyliśmy proces przenoszenia Twojej bazy danych z RDS do EC2. Ostatnim krokiem do zrobienia jest usunięcie naszego urządzenia podrzędnego RDS - spełniło swoje zadanie i można je usunąć.

W naszym następnym poście na blogu oprzemy się na tym. Przejdziemy przez scenariusz, w którym przeniesiemy naszą bazę danych z AWS/EC2 do oddzielnego dostawcy hostingu.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Dziesięć najlepszych narzędzi GUI MySQL

  2. Jak zamknąć połączenie sqlalchemy w MySQL?

  3. ClassCastException:java.math.BigInteger nie może być rzutowany na java.lang.Long podczas łączenia się z MySQL

  4. Zamiana wartości kolumn w MySQL

  5. Przegląd replikacji między klastrami