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.
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.
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.
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.
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í
Č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.
Ahoj,
díky za tip na kompresi a expire_logs_days, o tom jsem nevěděl. Na druhou stranu přesně vím, ve kterém binlogu se slave nachází, tak to likviduju skriptem naprosto přesně. Každopádně proč píšu - když nalejvám data na slave, buď úplně poprvé, anebo když se replikace rozsype, je možné si ušetřit ono čekání klientů při kopírování. Stačí mít adresář s databázemi na LVčku, dát READLOCK, udělat snapshot, potom hned unlock tables a data následně kopírovat na slejva ze snapshotu.
Při rozhození replikace a potřebě ji znovu nahodit se mi jako nejjednodušší časem ukázal postup:
- zastavit klienty
- vydumpovat DB na masteru, případně tam kde si myslím, že jsou data v pořádku
- dropnout DB na masteru -> dropne se na slavu
- natáhnout DB z dumpu -> natáhne se i na slav
Výhody a nevýhody ať si zváží každý sám.
Také bych vám doporučil podívat se na již dříve zmíněné nástroje http://www.percona.com/software/percona-toolkit/
Také bych si dovolil nabídnout skript na kontrolu kozistence, snad někoumu pomůže:
# Kontrola konzistence tabulek replikovane DB
TMPFILE=`mktemp`
read -sp "Heslo: " PSWD
echo -e "Porovnavam, muze to chvili trvat ... "
mk-table-checksum localhost h=,P= \
--user root --password $PSWD -d $TMPFILE
cat $TMPFILE | mk-checksum-filter
echo -e " ... porovnani bylo dokonceno, pokud se nic nezobrazilo, jsou DB identicke\n\n"
#rm $TMPFILE
Tomáš je autorem několika více či méně známých projektů jak z oblasti operačních systémů, tak internetu. V současnosti samozvaný expert na Linux, Bash, PHP a MySQL.
Přečteno 25 945×
Přečteno 23 975×
Přečteno 19 499×
Přečteno 18 283×
Přečteno 12 887×