Chciała bym teraz troszkę zmienić scenariusz wydarzeń i zastanowić się co musimy zrobić aby naprawić replikację master-master w MySQLu. Załóżmy, że mamy dwa serwery, których zmiany powinny być wzajemnie replikowane. Niestety zauważyliśmy, że zmiany z II serwera już od jakiegoś czasu nie są zatwierdzane na slavie z powodu błędu. Dodatkowym problemem jest rosnąca liczba plików relay logów na serwerze. Slave nie może ich usunąć bo zmiany w nich nie zostały zatwierdzone ale są cały czas od mastera pobierane nowe zmiany. Musimy zacząć myśleć co z tym zrobić aby się nie okazało, że za jakiś czas zacznie brakować nam miejsca.
Oto przykładowy błąd w tym wypadku:
mysql> show slave status \G *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 1.1.1.1 Master_User: replicate Master_Port: 3306 Connect_Retry: 60 Master_Log_File: host-mysql-01-bin.002539 Read_Master_Log_Pos: 902412914 Relay_Log_File: host-mysql-01-relay-bin.001829 Relay_Log_Pos: 461330748 Relay_Master_Log_File: host-mysql-01-bin.002399 Slave_IO_Running: Yes Slave_SQL_Running: No Replicate_Do_DB: Replicate_Ignore_DB: Replicate_Do_Table: Replicate_Ignore_Table: Replicate_Wild_Do_Table: Replicate_Wild_Ignore_Table: Last_Errno: 1452 Last_Error: Could not execute Write_rows event on table test.BATCH_STEP_EXECUTION; Cannot add or update a child row: a foreign key constraint fails (`test`.`BATCH_STEP_EXECUTION`, CONSTRAINT `JOB_EXEC_STEP_FK` FOREIGN KEY (`JOB_EXECUTION_ID`) REFERENCES `BATCH_JOB_EXECUTION` (`JOB_EXECUTION_ID`)), Error_code: 1452; handler error HA_ERR_NO_REFERENCED_ROW; the event's master log host-mysql-01-bin.002399, end_log_pos 508490809 Skip_Counter: 0 Exec_Master_Log_Pos: 508490445 Relay_Log_Space: 145560879727 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: NULL Master_SSL_Verify_Server_Cert: No Last_IO_Errno: 0 Last_IO_Error: Last_SQL_Errno: 1452 Last_SQL_Error: Could not execute Write_rows event on table test.BATCH_STEP_EXECUTION; Cannot add or update a child row: a foreign key constraint fails (`test`.`BATCH_STEP_EXECUTION`, CONSTRAINT `JOB_EXEC_STEP_FK` FOREIGN KEY (`JOB_EXECUTION_ID`) REFERENCES `BATCH_JOB_EXECUTION` (`JOB_EXECUTION_ID`)), Error_code: 1452; handler error HA_ERR_NO_REFERENCED_ROW; the event's master log host-mysql-01-bin.002399, end_log_pos 508490809 1 row in set (0.00 sec)
Napisze od razu, że zastosowanie SQL_SLAVE_SKIP_COUNTER=1 (a tu artykuł dlaczego jest to złe) w tym przypadku nie jest rozwiązaniem - według błędu ta zmiana próbuje się odwołać do elementu, który nie istnieje. Co jeśli takich zmian jest cała masa? Chcecie cały czas siedzieć przed komputerem i sprawdzać czy nie trzeba countera pominąć?! Najlepszym rozwiązaniem będzie odtworzenie całej replikacji. Aby to zrobić będziemy początkowo traktować 'zepsuty' slave jako nowy nod (http://db-diary.blogspot.com/2014/05/podpiecie-nowego-noda-do-replikacji-w.html).
No to co robimy:
- Przekierowywujemy cały ruch aplikacji na działającego noda - w naszym przypadku jest to II serwer.
- Generujemy dump z II serwera:
mysqldump -uroot -proot --skip-lock-tables --single-transaction --flush-logs --hex-blob --master-data=2 -A > dump.sql
UWAGA! Tutaj musicie założyć, że wszystkie bazy danych mają tabelkę transakcyjne - InnoDB. Jeśli używanie także MyISAM wymagane będzie zatrzymać aplikację, które powodują zamiany na działającym masterze (w naszym przypadku to oczywiście serwer II) lub wymusić lokowanie tabel w czasie robienia dumpa. Jeśli tego nie zrobicie w dumpie mogą pojawić się dane z 'późniejszych' insertów niż pozycja i plik binary logów na to wskazuje. - Zczytujemy pozycję mastera z dumpa
head dump.sql -n80 | grep "MASTER_LOG_POS" - Kopiujemy dump na I serwer
- Stopujemy replikację na oby dwóch serwerach przy pomocy polecenia:
STOP SLAVE;
Ponieważ w dumpie mamy wszystkie polecenia do odtworzenia całego klastra, musimy się upewnić, że w czasie importowania jego, żadne zmiany nie przeniosą się na slava. - Resetujemy slava na I serwerze
RESET SLAVE;
Przy pomocy tego polecenia wszystkie relay-logi zostaną usunięte i zostanie stworzony całkiem nowy. Informacje o replikacji także zostaną 'zapomniane' - pliki master.info i relay-log.info zostaną usunięte. Zawierają one informację o połączeniu i pozycjach w plikach. Warto je przekopiować na wypadek jak byśmy nie pamiętali nazwy użytkownika i jego hasła lub IP hosta potrzebnego do połączenia w replikacji. - Zaimportować dump
mysql -uroot -proot < dump.sql - Zczytujemy nazwę binarylogu i pozycję na serwerze I
SHOW MASTER STATUS; - Ustawiamy slava dla serwerów
CHANGE MASTER TO MASTER_HOST='HOST',MASTER_USER='replication',MASTER_PASSWORD='replication', MASTER_LOG_FILE='mysql-bin. 000002', MASTER_LOG_POS=504122;
Plik i pozycję dla serwera I zaczytaliśmy z dumpa (punkt 3), a dla serwera II z poprzedniego kroku (punkt 8). - Uruchamiany replikację
START SLAVE; - Potwierdzamy działanie replikacji
SHOW SLAVE STATUS \G
Może się zdarzyć, że mimo to replikacja i tak nie działa. Musicie się przyjrzeć dokładnie jakie zmiany mają być przenoszone - może to wynikać ze złego sposobu wykonania dumpa (popatrz na komentarz w punkcie 2). Załóżmy, że miało to miejsce i pojawił się następujący błąd:
mysql> show slave status \G *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 1.1.1.1 Master_User: replicate Master_Port: 3306 Connect_Retry: 60 Master_Log_File: host-03-bin.002549 Read_Master_Log_Pos: 130312481 Relay_Log_File: host-03-relay-bin.000002 Relay_Log_Pos: 33108 Relay_Master_Log_File: host-03-bin.002549 Slave_IO_Running: Yes Slave_SQL_Running: No Replicate_Do_DB: Replicate_Ignore_DB: Replicate_Do_Table: Replicate_Ignore_Table: Replicate_Wild_Do_Table: Replicate_Wild_Ignore_Table: Last_Errno: 1032 Last_Error: Could not execute Update_rows event on table test.BATCH_JOB_SEQ; Can't find record in 'BATCH_JOB_SEQ', Error_code: 1032; handler error HA_ERR_END_OF_FILE; the event's master log host-03-bin.002549, end_log_pos 33132 Skip_Counter: 0 Exec_Master_Log_Pos: 32951 Relay_Log_Space: 130313453 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: NULL Master_SSL_Verify_Server_Cert: No Last_IO_Errno: 0 Last_IO_Error: Last_SQL_Errno: 1032 Last_SQL_Error: Could not execute Update_rows event on table test.BATCH_JOB_SEQ; Can't find record in 'BATCH_JOB_SEQ', Error_code: 1032; handler error HA_ERR_END_OF_FILE; the event's master log host-03-bin.002549, end_log_pos 33132 1 row in set (0.00 sec)
Istnieje prawdopodobieństwo, że tych złych danych jest tak mało, że możemy ręcznie wyrównać zmiany, aż slave przestanie sypać błędami (wykonanie dumpa trwało na tyle krótko, że tych 'nadmiernych' zmian było mało). Ale jak to zrobić? Posłużymy się narzędziem masterbinlog. Ja postanowiłam przejżeć wszystkie zmiany w relay logach:
mysqlbinlog --start-position=0 --verbose --base64-output=DECODE-ROWS host-03-relay-bin.000002 > /tmp/binary.logs
Przeglądałam plik i szukałam wystąpienia znaków 'end_log_pos 33132'. W ten sposób znalazłam:
#140522 1:12:00 server id 173 end_log_pos 33108 Table_map: `test`.`BATCH_JOB_SEQ` mapped to number 5085 #140522 1:12:00 server id 173 end_log_pos 33132 Update_rows: table id 5085 flags: STMT_END_F ### UPDATE test.BATCH_JOB_SEQ ### WHERE ### @1=354422 ### SET ### @1=354423 # at 4309887
Sprawdziłam, że ten wiersz dla tego ID w tej tabeli nie istnieje, a właściwie istnieje ale ma inną wartość - dlatego slave nie jest w stanie zatwierdzić zmian. Co teraz zrobić?
- Zatrzymujemy slave
STOP SLAVE; - Wykonujemy taką zmiane na bazie danych aby dane do tego momentu były zgodne do zatwierdzenia przez slave:
UPDATE test.BATCH_JOB_SEQ SET ID = 354422 WHERE ID = X; - Wznawiamy slava
START SLAVE;
Brak komentarzy:
Prześlij komentarz