
Postęp nie stoi w miejscu, dlatego powody, dla których warto zaktualizować do najnowszych wersji MySQL, stają się coraz bardziej przekonywujące. Niedawno w jednym z naszych projektów nadszedł czas aktualizacji wygodnych klastrów Percona Server 5.7 do wersji 8. Wszystko to działo się na platformie Ubuntu Linux 16.04. Jak wykonać tę operację z minimalnym przestojem i z jakimi problemami się spotkaliśmy podczas aktualizacji — przeczytaj w tym artykule.
Przygotowanie
Każda aktualizacja serwera baz danych prawdopodobnie wiąże się z koniecznością dostosowania bazy: zmianami wymagań dotyczących limitów na zasoby systemowe oraz poprawkami w konfiguracjach bazy, które należy oczyścić z przestarzałych dyrektyw.
Przed aktualizacją obowiązkowo zapoznamy się z oficjalną dokumentacją:
- ;
- ;
- ;
- .
I przygotujemy plan działania:
- Skorygować pliki konfiguracyjne, usuwając przestarzałe dyrektywy.
- Sprawdzić kompatybilność za pomocą narzędzi.
- Zaktualizować bazy slave, instalując pakiet
percona-server-server. - Zaktualizować master, instalując ten sam pakiet.
Przyjrzymy się każdemu punktowi planu i zobaczymy, co może pójść nie tak.
WAŻNE! Procedura aktualizacji klastra MySQL opartego na Galera ma swoje niuanse, które nie zostały opisane w artykule. Nie należy wykorzystywać tej instrukcji w takim przypadku.
Część 1: Sprawdzenie konfiguracji
W wersji 8 MySQL usunięto query_cache. W rzeczywistości został on już w wersji 5.7, ale teraz został . W związku z tym konieczne jest usunięcie powiązanych dyrektyw. A do cachowania zapytań można teraz używać zewnętrznych narzędzi — na przykład, .
Również w konfiguracji znalazły się przestarzałe dyrektywy dotyczące innodb_file_format. Jeśli w MySQL 5.7 istniała możliwość wyboru formatu InnoDB, to wersja 8 działa już .
Naszym wynikiem jest usunięcie następujących dyrektyw:
-
query_cache_type,query_cache_limitiquery_cache_size; -
innodb_file_formatiinnodb_file_format_max.
Aby przeprowadzić test, wykorzystamy obraz Docker Percona Server. Konfigurację serwera umieścimy w katalogu mysql_config_test, a obok stworzymy katalogi na dane i logi. Przykład testu konfiguracji percona-server:
mkdir -p {mysql_config_test,mysql_data,mysql_logs}
cp -r /etc/mysql/conf.d/* mysql_config_test/
docker run --name some-percona -v $(pwd)/mysql_config_test:/etc/my.cnf.d/ -v $(pwd)/mysql_data/:/var/lib/mysql/ -v $(pwd)/mysql_logs/:/var/log/mysql/ -e MYSQL_ROOT_PASSWORD=${MYSQL_PASSWORD} -d percona:8-centosWynik: w logach Docker lub w katalogu z logami — w zależności od twoich konfiguracji — pojawi się plik z opisem problematycznych dyrektyw.
Oto co mieliśmy:
2020-04-03T12:44:19.670831Z 0 [Warning] [MY-011068] [Server] Składnia 'expire-logs-days' jest przestarzała i zostanie usunięta w przyszłym wydaniu. Proszę używać zamiast tego binlog_expire_logs_seconds.
2020-04-03T12:44:19.671678Z 0 [Warning] [MY-013242] [Server] --character-set-server: 'utf8' jest obecnie aliasem dla zestawu znaków UTF8MB3, ale w przyszłym wydaniu będzie aliasem dla UTF8MB4. Proszę rozważyć użycie UTF8MB4, aby być jednoznacznym.
2020-04-03T12:44:19.671682Z 0 [Warning] [MY-013244] [Server] --collation-server: 'utf8_general_ci' jest porządkiem przestarzałego zestawu znaków UTF8MB3. Proszę rozważyć użycie UTF8MB4 z odpowiednim porządkiem. W ten sposób musieliśmy jeszcze rozwiązać kwestie kodowania i zastąpić przestarzałą dyrektywę expire-logs-days.
Część 2: Sprawdzenie działających instalacji
W dokumentacji dotyczącej aktualizacji znajdują się 2 narzędzia do sprawdzania bazy danych pod kątem zgodności. Ich użycie pomaga administratorowi sprawdzić zgodność istniejącej struktury danych.
Zacznijmy od klasycznego narzędzia mysqlcheck. Wystarczy je uruchomić:
mysqlcheck -u root -p --all-databases --check-upgradeJeśli nie wykryto problemów, narzędzie zakończy działanie z kodem 0:

Ponadto, w nowoczesnych wersjach MySQL dostępne jest narzędzie (w przypadku Percony to pakiet percona-mysql-shell). Jest to zamiennik klasycznego klienta mysql i łączy w sobie funkcje klienta, edytora kodu SQL oraz narzędzia administracyjnego MySQL. Aby sprawdzić serwer przed aktualizacją, można przez nią wykonać następującą komendę:
mysqlsh -- util check-for-server-upgrade { --user=root --host=1.1.1.1 --port=3306 } --config-path=\/etc\/mysql\/my.cnfOto jakie uwagi otrzymaliśmy:

W sumie nic krytycznego — tylko ostrzeżenia dotyczące kodowania. (patrz poniżej)Ogólny wynik działania:

Stwierdziliśmy, że aktualizacja powinna przebiec bez problemów.
Uwaga o ostrzeżeniach powyżej, które wskazują na problemy z kodowaniem. Rzecz w tym, że UTF-8 w MySQL do niedawna ponieważ przechowywała tylko 3 bajty zamiast 4. W MySQL 8 postanowiono to w końcu alias utf8 wkrótce będzie prowadził do kodowania utf8mb4,a stare kolumny w tabelach staną się utf8mb3.W przyszłości kodowanie utf8mb3. zostanie usunięte, ale nie w tej wersji. Dlatego postanowiliśmy poprawić kodowania już w działającej instalacji DBMS, po jej aktualizacji.
Część 3: Aktualizacja serwerów
Co może pójść nie tak, gdy mamy tak wspaniały plan?.. Doskonale rozumiejąc, że szczegóły mogą się zdarzyć, pierwszy eksperyment przeprowadziliśmy na klastrze deweloperskim MySQL.
Jak już wspomniano, porusza kwestię aktualizacji serwerów MySQL z replikami. Istotą jest to, że najpierw należy aktualizować wszystkie repliki (slave), ponieważ MySQL 8 potrafi replikować z mastera wersji 5.7. Pewna trudność polega na tym, że używamy trybu master master, gdy zdalny master znajduje się w trybie tylko do odczytu. To oznacza, że rzeczywisty ruch produkcyjny trafia do jednego centrum danych, a drugie jest rezerwowe.
Topologia wygląda następująco:

Aktualizacja powinna rozpocząć się od replik mysql replica dc 2, mysql master dc 2 i mysql replica dc 1, a zakończyć — serwerem mysql master dc 1. Dla większej pewności zatrzymaliśmy wirtualne maszyny, zrobiliśmy ich migawki, a tuż przed aktualizacją zatrzymaliśmy replikację poleceniem STOP SLAVE. W pozostałych kwestiach aktualizacja wygląda tak:
- Każdą replikę uruchamiamy ponownie, dodając do konfiguracji 3 opcje:
skip-networking,skip-slave-start,skip-log-bin. Chodzi o to, że aktualizacja bazy generuje binarne logi z aktualizacją tabel systemowych. Te dyrektywy zapewniają, że w bazie nie będą zmieniane dane aplikacji, a do binarnych logów nie trafią informacje o aktualizacji tabel systemowych. To pozwoli uniknąć problemów przy wznowieniu replikacji. - Instalujemy pakiet
percona-server-server. Warto zauważyć, że w wersji MySQL 8 nie konieczne jest uruchomienie poleceniamysqlupgradepo aktualizacji serwera. - Po pomyślnym uruchomieniu jeszcze raz uruchamiamy serwer — już bez parametrów, które dodano w pierwszym punkcie.
- Upewniamy się, że replikacja działa poprawnie: sprawdzamy
SHOW SLAVE STATUSi patrzymy, że tabele z licznikami w bazie aplikacji są aktualizowane.
Wszystko to wygląda dość prosto: aktualizacja dev zakończyła się powodzeniem. W porządku, można spokojnie planować nocną aktualizację dla produkcji.
Nie było smutku — prod aktualizowaliśmy
Jednak przeniesienie udanego doświadczenia z dev do produkcji nie obyło się bez niespodzianek.
Na szczęście sam proces aktualizacji rozpoczyna się od replik, dlatego, gdy napotkaliśmy trudności, zatrzymaliśmy prace i przywróciliśmy replikę z migawki. Badanie problemów przenieśliśmy na następny poranek. W logach pojawiły się następujące wpisy:
2020-01-14T21:43:21.500563Z 2 [ERROR] [MY-012069] [InnoDB] tabela: t1 ma 19 kolumn, ale słownik InnoDB ma 20 kolumn
2020-01-14T21:43:21.500722Z 2 [ERROR] [MY-010767] [Server] Błąd podczas naprawy danych SE dla db1.t1
2020-01-14T21:43:24.208365Z 0 [ERROR] [MY-010022] [Server] Nie udało się zainicjować tabel DD.
2020-01-14T21:43:24.208658Z 0 [ERROR] [MY-010119] [Server] Przerywanie Badanie archiwów różnych newsletterów w Google doprowadziło do zrozumienia, że problem ten występuje z powodu . Chociaż w rzeczywistości jest to raczej błąd narzędzi mysqlcheck i mysqlsh.
Okazuje się, że w MySQL zmieniono sposób prezentacji danych dla pól dziesiętnych (int, tinyint itd.), dlatego wewnątrz mysql-server używany jest inny sposób ich przechowywania. Jeśli Twoja baza danych początkowo była w wersji 5.5 lub 5.1, a następnie zaktualizowano ją do 5.7, to może być konieczne przeprowadzenie OPTIMIZE aktualizacji dla niektórych tabel. W takim przypadku MySQL zaktualizuje pliki z danymi, przekształcając je na aktualny format przechowywania.
Można to również sprawdzić za pomocą narzędzia mysqlfrm:
mysqlfrm --diagnostic -vv /var/lib/mysql/db/table.frm
...
'field_length': 8,
'field_type': 246, # format pola
'field_type_name': 'decimal',
'flags': 3,
'flags_extra': 67,
'interval_nr': 0,
'name': 'your_decimal_column',
... Jeśli field_type jeśli wartość wynosi 0, to w tabeli używany jest stary typ — należy przeprowadzić OPTIMIZE. Jednak jeśli jest to wartość 246 — masz już nowy typ. Więcej na temat typów można znaleźć w .
Co więcej, w omawiana jest druga możliwa przyczyna, która umknęła naszej uwadze, czyli brak tabel InnoDB w tabeli systemowej INNODB_SYS_TABLESPACES, jeśli te tabele były tworzone w wersji 5.1. Aby uniknąć problemów podczas aktualizacji, można skorzystać z .
Dlaczego więc nie mieliśmy takich problemów na dev? Baza jest tam okresowo kopiowana z produkcji — w ten sposób tabele są na nowo tworzone.
Niestety, na rzeczywistej dużej bazie danych nie da się po prostu wykonać ogólnego OPTIMIZE. W tej sytuacji pomoże percona-toolkit: do operacji online OPTIMIZE znakomicie nadaje się narzędzie pt-online-schema-change.
Zaktualizowany plan wygląda następująco:
- Przeprowadzić optymalizację wszystkich tabel.
- Przeprowadzić aktualizację baz danych.
Aby to sprawdzić i jednocześnie ustalić czas aktualizacji, wyłączyliśmy jedną z replik, a dla wszystkich tabel uruchomiliśmy następujące polecenie:
pt-online-schema-change --critical-load Threads_running=150 --alter "ENGINE=InnoDB" --execute --chunk-size 100 --quiet --alter-foreign-keys-method auto h=127.0.0.1,u=root,p=${MYSQL_PASSWORD},D=db1,t=t1Aktualizacja tabel odbywa się bez długotrwałych blokad dzięki temu, że narzędzie tworzy nową tymczasową tabelę, do której kopiuje dane z głównej tabeli. W momencie, gdy obie tabele są identyczne, oryginalna tabela jest blokowana i zastępowana nową. W naszym przypadku testowy uruchomienie wykazało, że aktualizacja wszystkich tabel zajmie około doby, ale kopiowanie danych powodowało zbyt duże obciążenie dysków.
Aby temu zapobiec, na produkcji dodaliśmy do polecenia argument --sleep o wartości 10 — ten parametr reguluje czas oczekiwania po przeniesieniu partii danych do nowej tabeli. Dzięki temu można zmniejszyć obciążenie, jeśli rzeczywiście uruchomiona aplikacja jest wymagająca pod względem czasu odpowiedzi.
Po wykonaniu optymalizacji aktualizacja przebiegła pomyślnie.
… ale nie do końca!
Już po pół godziny od aktualizacji klient zgłosił problem. Baza działała bardzo dziwnie: okresowo zaczynały się zerwania połączeń. Oto jak to wyglądało w monitoringu:

Na zrzucie ekranu widać piłokształtny wykres, związany z tym, że część wątków serwera MySQL okresowo padała z błędem. W aplikacji pojawiły się błędy:
[PDOException] SQLSTATE[HY000] [2002] Połączenie odrzuconeSzybki przegląd logów wykazał, że demon mysqld nie mógł uzyskać wymaganych zasobów od systemu operacyjnego. Zmagać się z błędami, odkryliśmy w systemie "bezpańskie" pliki polityki apparmor:
# dpkg -S /etc/apparmor.d/cache/usr.sbin.mysqld
dpkg-query: no path found matching pattern /etc/apparmor.d/cache/usr.sbin.mysqld
# dpkg -S /etc/apparmor.d/local/usr.sbin.mysqld
dpkg-query: no path found matching pattern /etc/apparmor.d/local/usr.sbin.mysqld
# dpkg -S /etc/apparmor.d/usr.sbin.mysqld
mysql-server-5.7: /etc/apparmor.d/usr.sbin.mysqld
# dpkg -l mysql-server-5.7
rc mysql-server-5.7 5.7.23-0ubuntu0.16.04.1 amd64Te pliki powstały podczas aktualizacji do MySQL 5.7 kilka lat temu i należą do usuniętego pakietu. Usunięcie plików i ponowne uruchomienie usługi apparmor rozwiązało problem:
systemctl stop apparmor
rm /etc/apparmor.d/cache/usr.sbin.mysqld
rm /etc/apparmor.d/local/usr.sbin.mysqld
rm /etc/apparmor.d/usr.sbin.mysqld
systemctl start apparmorNa zakończenie
Każda, nawet najprostsza operacja, może prowadzić do niespodziewanych problemów. A nawet posiadanie przemyślanego planu nie zawsze gwarantuje oczekiwany wynik. Teraz w każdy plan aktualizacji naszej drużyny wchodzi również obowiązkowe czyszczenie zbędnych plików, które mogły pojawić się w wyniku ostatnich działań.
Za tę mało profesjonalną grafikę chciałbym serdecznie podziękować firmie Percona za ich doskonałe produkty!

P.S.
Przeczytaj także na naszym blogu:
- «»;
- «»;
- «»;
- «»;
- «».
Źródło: habr.com
