Hlavní navigace

MySQL Master-Slave replikace

26. 4. 2012 14:32 (aktualizováno) Tomas Matějíček

Dnes jsem si na rootu přečetl článek o master-slave replikaci. Některé věci tam uvedené jsou zbytečné, jiné zavádějící, a dost podstatné údaje chybí, proto tímhle blog postem věc napravím. Provozuju Master-Slave replikaci už nekolik let na devíti serverech a za tu dobu jsem už nějaké praktické zkušenosti nasbíral.

    Popis funkce replikace

    Jak replikace funguje? Na master serveru se každý SQL dotaz, který uživatelé vykonají, ukládá do binárního logu, což je velký soubor někde na disku, obvykle v adresáři s daty mysql. Slave servery se po síti k master mysql připojují a tento soubor sekvenčně čtou a vykonávají ty samé SQL dotazy na své lokální kopii databáze. Tím vzniká na slave serveru kopie dat z masteru, s určitým spožděním (obvykle max secundy). Pro replikaci musíme něco málo nastavit, a pak slave serveru předat informaci, z kterého souboru má na masteru začít číst a od jaké pozice, toť vše.

    Výhodou replikace, kromě těch popsaných v článku, je například zálohování. Pokud je databáze rozsáhlá, může být zálohování časově velmi náročnou operací, a po čas zálohování musí klienti čekat, což je nepřijatelné. Proto je vhodné databázi na pozadí replikovat jinam, a zálohu provádět z dat na slave serveru – pokud bude trvat hodinu, nikomu to nebude vadit, replikace jednoduše počká a po dokončení zálohy se zase obnoví od místa kde přestala, žádný z klientů nebude nijak ovlivněn.

      Nastavení masteru

      Základní nastavení masteru je jednoduché, a bylo vesměs popsáno v článku. První věcí je povolit networking (zrušit případné skip-networking, a vypustit uváděný bind-address), aby se k masteru mohly slave servery připojit. Toto nastavení může být podle použité distribuce buť v my.cnf, nebo v rc skriptu který databázi startuje (jako argument pro mysqld). Dále pak je třeba nastavit zmíněné server-id (v my.cnf, ID musí být unikátní) a vytvořit uživatele s příslušnými právy pro replikaci (sql příkazem, v tabulce mysql.user). Nutno dodat, že replikační uživatel nepotřebuje žádná jiná oprávnění. Pokud uživatele přidáte či editujete přímým zásahem do tabulky mysql.user, je nutné změněné hodnoty naloudovat do běžícího mysqld procesu pomocí sql dotazu FLUSH PRIVILEGES.

      V článku uvedené binlog-ignore-db=mysql je vhodné ve speciálních případech, kdy opravdu nechci změny v databázi mysql replikovat; pokud ale konkrétně toto není naším explicitním cílem, doporučuji tento krok ignorovat a nechat replikovat vše. Nicméně je binlog-ignore-db velmi užitečný pro zamezení replikování databází či tabulek, které opravdu replikovat nechceme, ušetří se tím jak bandwidth po síti tak i load a místo na disku na slave serveru.

      Nutno zmínit několik dalších užitečných parametrů pro nastavení na masteru:

      expire_logs_days=5 … na masteru určí, po jak dlouhé době se budou mazat binárlní logy. Pár dnů je optimální hodnota

      binlog-format=mixed… umožňuje bezpečně replikovat i SQL dotazy, které by jinak replikovat nešly, jako UPDATE tbl SET a=1 LIMIT 1. Místo zalogování takového SQL příkazu dojde k logování a následné replikaci rovnou provedené změny. Pokud tohle zapomenete, použije se defaultně statement-based replikace – uvedený SQL dotaz se na slave serveru provede, ale nikdo nezaručuje že ovlivní přesně ty samé řádky jako na masteru.

        Nastavení slave

        Co se týká nastavení na slave serveru, tak ani master-host ani jiné věci není potřeba nikde konfigurovat v my.cnf, to je v článku uvedeno mylně. Stačí nastavit každému slave jeho unikátní server-id (v my.cnf) a pak replikaci nastartovat příkazem CHANGE MASTER […]. MySQL si potřebné parametry uloží, a následně je i aktualizuje. Použití replicate-do-db také není nutné, v situaci kdy chci replikovat vše, resp. vše kromě tabulek či databází uvedených pro ignorování. Co však v článku chybělo byly dva parametry které považuji za nezbytné:

        slave_compressed_protocol=1 … umožní kompresi dat posílaných po síti, a tudíž rychlejší replikaci (v drtivé většině případů je čas na kompresi a dekompresi kratší, než čas potřebný navíc pro přenesení většího bloku dat po síti). Velmi doporučuji, především pokud je slave server připojen k masteru rychlostí pod 100Mbit/s

        slave_net_timeout=10 … udává počet sekund pro timeout. Ve chvíli, kdy 10 vteřin nepřijdou z masteru žádná data, považuje se spojení za porušené a slave se přikonektí znovu. Defaultní hodnota je v řádech hodin, což je absurdní. Vezmu-li v úvahu, že master server provádí nějaké zápisy téměř nepřetržitě, je 10 sekund bezpečná hodnota.

          Start replikace existujících databázi za chodu

          Pokud chci začít replikovat existjící živou databázi, postupuji následovně:
          - nastavím konfiguraci pro master a restartnu master (stačí restart mysqld procesu, netřeba rebootovat)
          - nastavím konfiguraci pro slave
          - na masteru z jedné konzole spustím sql příkaz „FLUSH TABLES WITH READ LOCK“
          - po uzamknutí natvrdo zkopíruju (z jiné konzole) všechny mysql data někam bokem, nebo přimo na slave
          - během kopírování si ještě poznačím (z první konzole) odpověď na sql dotaz „SHOW MASTER STATUS“
          - nutno upozornit že během kopírování musejí klienti na databáze čekat ve frontě
          - po dokončení kopírování dat ukončím spojení na konzoli 1, tím vše zas odemknu
          - přenesu po sítí nakopírované data na slave server (pokud jsem je rovnou nekopíroval)
          - spustím slave mysqld proces
          - provedu na slave serveru příkaz CHANGE MASTER s příslušnými parametry, tím se replikace spustí
          - je velmi vhodné, aby verze MySQL byla na všech serverech stejná, nebo aspoň kompatibilní

            Řešení poruch

            Čas od času se replikace naruší, ať už chybou síťové komunikace, nebo neočekávaným restartem některého serveru. Vždy je samozřejmě možné provést smazání slave databáze a přenesení dat z masteru, jak je popsáno výše, ale některé situace se dají řešit jednodušeji.

            Při přerušení replikace kvůli něčemu na síti obvykle stačí počkat na vypršení timeoutu, nebo na slavu ručně spustit STOP SLAVE IO_THREAD; START SLAVE IO_THREAD;

            Pokud se replikace přerušila kvůli chybě při provádění nějakého dotazu, informuje o tom mysql ve výpisu z SHOW SLAVE STATUS\G na slave serveru – operátor pak může vyhodnotit, jestli je dotaz opravdu nutné provést, případně jej může provést ručně. Pak pomocí SET GLOBAL sql_slave_skip_counter = 1 nastaví, aby se problematický dotaz přeskočil, a znovu replikaci spustí.

            Pokud nic nezabere, nebo je kvůli porušené slave databázi (např výpadkem proudu) nutné kopírovat vše z masteru, jde taky použít malý workaround – mám-li víc slave serverů, locknu data na jednom z nich a zkopíruju je odtamtud, a z výpisu SHOW SLAVE STATUS\G zjistím i pozice v binárním logu na masteru. Jen pozor, nesplést si to s pozicí v kopii binárního logu na slave.

            Sdílet