Replikacja danych na kilka serwerów może okazać się bardzo pomocna w kilku przypadkach. Przy średnim obciążeniu zapewni nam ciągły dostęp do danych, natomiast przy dużym obciążeniu zapytaniami pozwoli tak wysterować ruchem (za pomocą dodatkowego oprogramowania), aby rozłożyć zapytania na różne serwery, co za tym idzie odciążyć maszynę główną.
Przyjmijmy, że posiadamy kilka maszyn, obciążenie bazy danych nie jest spore, a my, jako leniwi administratorzy w razie awarii nie chcemy osiwieć, odzyskiwać danych ze zrzutów, tylko – po najmniejszej linii oporu przełączyć serwery między sobą i zając się naprawą awarii.
Dla konkretnego przykładu posiadamy w sieci trzy maszyny:
- db-master0 10.88.12.23 (używany jako serwer główny)
Na masterze w pliku /etc/mysql/mariadb.conf.d/50-server.cnf
(Raspi-os 2021-03-04 z zainstalowaną MariaDB) należy zastąpić linię "bind-address = 127.0.0.1"
(localhost) przez "bind-address = 0.0.0.0"
(all). Następnie powinieneś zrestartować serwer MySQL:$ sudo service mariadb restart
- db-slave0 192.168.112.120 (używany jako serwer podrzędny 1)
- db-slave1 172.16.6.14 (używany jako serwer podrzędny 2)
Po uruchomieniu serwera db-master0, utworzeniu baz danych, czy też po prostu – w każdej chwili jego działania, w momencie, gdy będziemy chcieli rozpocząć replikację, musimy dodać odpowiednie opcje konfiguracyjne do pliku my.cnf
- server-id = 1
- log-bin = /var/log/mysql/mysql-bin.log
- max_binlog_size = 100M
- sync_binlog = 1
- innodb_flush_log_at_trx_commit=1
- expire_logs_days = 14
Opcje innodb_flush_log_at_trx_commit = 1 oraz sync_binlog = 1, przy używaniu silnika InnoDB powinny zadbać o delikatność dla danych podczas nieplanowanych ugaszeń serwera baz danych, server-id musi być unikalne. Po zapisaniu zmian możemy zrestartować serwer mysql na db-master0.
Po restarcie na db-master0 musimy utworzyć użytkownika (lub kilku) oraz nadać mu (im) uprawnienia do replikacji danych:
CREATE USER 'slaveuser'@'db-slave0' IDENTIFIED BY 'bardzotajneilosowehaslonajlepiej2znakowe';
GRANT REPLICATION SLAVE ON *.* TO 'slaveuser'@'db-slave0';
CREATE USER 'slaveuser'@'db-slave1' IDENTIFIED BY 'równietajnehaslonajlepiejzwielkimiliterami';
GRANT REPLICATION SLAVE ON *.* TO 'slaveuser'@'db-slave1';
FLUSH PRIVILEGES;
Dla wygody i zrozumienia zapisałem zamiast adresów odpowiednie hosty (szybciej jest używać adresów IP, ale wygodniej dla leniwych adminów hostów). Należy upewnić się więc, że hosty db-master0, db-slave0 oraz db-slave1 są rozwiązywalne (i widoczne każda dla każdej), najlepiej dodając do pliku hosts odpowiednie wpisy oraz upewniając się, że host.conf ma odpowiednie wartości. Najlepiej jest po prostu zarówno z hosta db-slave0 jak i db-slave1 przetestować połączenia z mysqlem:
mysql -u slaveuser -h db-master0 -p
Jeżeli udało nam się połączyć z obu hostów slave – jest szansa, że wszystko zadziała poprawnie. Teraz należy dodać odpowiednie opcje w plikach my.cnf serwerów slave. Dla db-slave0:
- server-id = 2
- log-bin = /var/log/mysql/mysql-bin.log
- sync_binlog = 1
- report-host = db-slave0
Oraz dla serwera db-slave1:
- server-id = 3
- log-bin = /var/log/mysql/mysql-bin.log
- sync_binlog = 1
- report-host = db-slave1
Następne cztery kroki należy wykonać relatywnie prędko (w przypadku, gdy nie tworzymy replikacji w oknie serwisowym lub po prostu dla prestiżu dostępności naszych usług).
- Na serwerze db-master musimy zablokować możliwość dodawania, usuwania i zmian danych do naszych baz:
FLUSH TABLES WITH READ LOCK;
- Teraz musimy odczytać i na chwilę zapamiętać aktualny plik dziennika (File: mysql-bin.XXX) oraz aktualną pozycję ostatniego zdarzenia z dziennika (Position: YYY)
show master status G
- Możemy przystąpić do zrzucania bazy/baz danych z serwera master (tutaj zrzucamy wszystkie bazy danych wraz z danymi o użytkownikach oraz procedurami):
mysqldump --master-data --add-drop-table --quick --routines --all-databases -u root -p > /tmp/db-master0-alldatabases.sql
- Po wykonaniu zrzutu możemy odblokować tabele:
UNLOCK TABLES;
Polecam napisać sobie szybki skrypt powłoki w celu zminimalizowania przestojów spowodowanych człowiekiem:
#!/bin/bash
# Wywołanie: $0 db-user db-password
/bin/echo "FLUSH TABLES WITH READ LOCK;" | /usr/bin/mysql -u$1 -p$2
/usr/bin/mysqldump --master-data --add-drop-table --quick --routines --all-database -u$1 -p$2 > /tmp/db-master0-alldatabases.sql
/bin/echo "SHOW MASTER STATUS \G" | /usr/bin/mysql -u$1 -p$2
/bin/echo "UNLOCK TABLES;" | /usr/bin/mysql -u$1 -p$2
Wykonany zrzut musimy teraz przerzucić na serwery db-slave0 oraz db-slave1 – można to zrobić dowolnie, najwygodniej udostępnić po NFS lub przenieść korzystając z SCP:
scp /tmp/db-master0-alldatabases.sql user@db-slave0:/tmp/
scp /tmp/db-master0-alldatabases.sql user@db-slave1:/tmp/
Po przerzuceniu lub udostępniueniu plików należy zalogować się do konsoli mysql serwera db-slave0 (i db-slave1), a następnie wykonać następujące kroki:
- Dla servera db-slave0:
- Zatrzymać serwer db-slave0:
STOP SLAVE;
- Zaimportować plik ze zrzuconymi bazami z db-mastera:
SOURCE /tmp/db-master0-alldatabases.sql
- Ustawić na serwerze db-slave0 informację o serwerze master, aktualnym dzienniku binarnym i pozycji ostatniego rekordu w dzienniku:
CHANGE MASTER TO MASTER_HOST = 'db-master0', MASTER_USER = 'slaveuser', MASTER_PASSWORD = 'bardzotajneilosowehaslonajlepiej2znakowe', MASTER_LOG_FILE = 'mysql-bin.XXX', MASTER_LOG_POS = YYY;
- Uruchomić serwer db-slave0:
START SLAVE;
- Spradzić czy działa:
SHOW SLAVE STATUS \G
- Zatrzymać serwer db-slave0:
- Dla servera db-slave1:
- Zatrzymać serwer db-slave1:
STOP SLAVE;
- Zaimportować plik ze zrzuconymi bazami z db-mastera:
SOURCE /tmp/db-master0-alldatabases.sql
- Ustawić na serwerze db-slave1 informację o serwerze master, aktualnym dzienniku binarnym i pozycji ostatniego rekordu w dzienniku:
CHANGE MASTER TO MASTER_HOST = 'db-master0', MASTER_USER = 'slaveuser', MASTER_PASSWORD = 'równietajnehaslonajlepiejzwielkimiliterami', MASTER_LOG_FILE = 'mysql-bin.XXX', MASTER_LOG_POS = YYY;
- Uruchomić serwer db-slave1:
START SLAVE;
- Spradzić czy działa:
SHOW SLAVE STATUS \G
- Zatrzymać serwer db-slave1:
Jeżeli ostatnia komenda zwróci dwa razy Yes dla parametrów Slave_IO_Running oraz Slave_SQL_Running, udało nam się wykonać replikację.
Warto na serwerach slave przerestartować demona mysql w celu określenia czy wstaje bez problemów. W przypadku gdy replikujemy dane z różnych wersji serwera mysql, sczególnie gdy na serwerze db-master0 jest starsza wersja od tych, zainstalowanych na db-slave0 i db-slave1, na serwerach slave może pojawić się komunikat: Cannot load from mysql.proc. The table is probably corrupted. Uleczyć to można poprzez wymuszoną aktualizację tabel do nowej wersji: mysql_upgrade -u root -p -force
W skrócie tyle. Aby spokojnie położyć się spać warto od czasu do czasu sprawdzać stan replikacji na serwerach slave, używając składni: SHOW SLAVE STATUS G. W przypadku zatrzymania replikacji i chęci jej najprostrzego przywrócenia zalecam wykonać wszystkie kroki z pominięciem konfiguracji w plikach my.cnf
Co w przypadku awarii naszego db-master0? Wybieramy sobie jeden z naszych serwerów slave (na przykład db-slave0), wyłączamy na nim replikację: STOP SLAVE; Następnie dodajemy do pliku my.cnf opcję: skip-slave-start, wymieniamy jego adres IP i naprawiamy usterkę na serwerze db-master0.