Zapisivanje na oba „master“ servera u „master-master“ replikaciji

Zapisivanje na oba „master“ servera uglavnom nije dobra ideja. Ako pokušavamo napraviti sigurno zapisivanje na oba „master“ servera u istom trenutku, tada mogu nastati mnogi problemi od kojih se ne mogu svi riješiti.

U MySQL 5.0 sustavu, dvije konfiguracijske varijable servera pomažu u rješavanju problema sukoba „AUTO_INCREMENT“ primarnih ključeva. Varijable su „auto_increment_increment“ i „auto_increment_offset“.

Ipak ovo ne rješava sve probleme koje ćemo imati s dva „master“ servera kod kojih se na oba zapisuju podaci. To rješava samo problem automatskog povećanja, koji je samo mali podskup konfliktnih zapisivanja koje ćemo imati. Ustvari, to zapravo dodaje nekoliko novih problema:

-To čini teže micanje servera u replikacijskoj topologiji.
-Troši prostor ključeva s potencijalnim predstavljanjem razmaka između brojeva.
-Ne pomaže sve dok sve tablice nemaju „AUTO_INCREMENT“ primarne ključeve, i
nije uvijek dobra ideja koristiti „AUTO_INCREMENT“ primarne ključeve
univerzalno.
Postoji nekoliko načina za generiranje primarnih ključeve koji neće biti u konfliktu. Jedan način je kreiranje primarnog ključa koji će se sastojati od dva ključa. Prva vrijednost može biti ID servera. Ovo radi dobro ali čini primarni ključ dužim.

Drugi način je koristiti primarni ključ s jednom vrijednosti i koristiti bitove najveće značajnosti („high bits“) za spremanje ID¬a servera. To se može napraviti s jednostavnim lijevim pomakom (ili množenjem) i zbrajanjem. Problem s ovom metodom je što trebamo vanjski način za generiranje vrijednosti, zato jer „AUTO_INCREMENT“ to ne može učiniti za nas. Ne smije se koristiti „@@server_id“ za unos ID¬a servera jer će se dobiti drugačiji rezultati na „slave“ serveru.

Postoje i drugi načini za rješavanje ovog problema, ali se preporuča ponovo dizajniranje aplikacije tako što će postojati samo jedan „master“ server na kojeg će se zapisivati podaci.

Pretjerano zaostajanje replikacije
Zaostajanje replikacije je problem koji se često ponavlja. Dobro je dizajnirati aplikacije tako što će tolerirati ponešto kašnjenja na „slave“ serverima. Ako sustav ne može funkcionirati s kašnjenjem „slave“ servera, možda je bolje ne koristiti replikaciju u nekim slučajevima. Ali ipak postoje neki načini kako možemo „slave“ servere držati u koraku s „master“ serverom.

Čak i brzi „slave“ server s mnogo diskova, procesora i memorije može lako početi kasniti za „master“ serverom, jer jedna dretva obično koristi samo jedan procesor i jedan disk efikasno. Ustvari, svaki server treba biti najmanje moćan kao „master“ server.

Zaključavanje na „slave“ serveru je isto problem. Upiti pokrenuti na „slave“ serveru mogu postaviti zaključavanja koja blokiraju dretvu replikacije. Zato što se replikacija izvodi kao jedna dretva, dretva replikacije neće biti u mogućnosti obavljati druge poslove dok čeka.

Replikacija teži zaostajanju na dva načina: kašnjenje uzrokovano dostizanjem ili povećavanje kašnjenja zbog prethodnog kašnjenja. Prošlo kašnjenje je vjerojatno uzrokovano s upitima koji su spori, ali kada server jednom počne kasniti tada kašnjenje mogu povećati i brzi upiti.

Nažalost trenutno ne postoji način koji bi pokazao kako blizu je „slave“ server svojim maksimalnim kapacitetima. Ako su učitavanja perfektno jednolika u svakom trenutku, „slave“ serveri bi trebali biti učinkoviti na 99% kapaciteta isto dobro kao i na 10% kapaciteta, a kada dosegnu 100% kapaciteta, iznenada će početi zaostajati. U stvarnosti, učitavanje nije pravilno pa kada će se „slave“ server približiti svojim maksimalnim kapacitetima zapisivanja, tada će se vjerojatno vidjeti povećano kašnjenje na kraju učitavanja.

Povećano kašnjenje je upozoravajući znak koji nas opominje na zaostajanje „slave“ servera tako jako što na kraju učitavanja oni više neće biti u koraku s „master“ serverom. Grubo mjerilo kako smo blizu vrhu kapaciteta je promišljeno zaustavljanje SQL dretve na kratko, zatim ponovo pokretanje pa vidimo koliko dugo treba dretvi za uloviti „master“ server.

Dobar način za otkrivanje zašto repliciranje kasni je zapisivanje upita u dnevnik na „slave“ serveru i korištenje alata za analizu dnevnika. Ono što uzrokuje sporost replikacije na „master“ serveru ne mora nužno uzrokovati kašnjenja na „slave“ serveru, zato jer „slave“ server i „master“ server imaju vrlo različite profile učinkovitosti. Najbolji način za učiniti ovu analizu je omogućiti zapisivanje najsporijeg upita na „slave“ serveru nakratko. Standardni MySQL zapis najsporijeg upita u dnevnik ne zapisuje spore upite koje izvršava dretva „slave“ servera, pa se ne može vidjeti koji su upiti spori dok se oni repliciraju. Ovo pitanje rješava zakrpa „microsecond slow¬log“.

Osim kupnje bržih diskova i procesora, ne postoji mnogo načina za smanjiti zaostajanje. Svakako je potrebno osloboditi „slave“ server nepotrebnog posla. Jedna jednostavna promjena je konfiguriranje InnoDB mehanizma za osvježavanje promjena na disku rjeđe, tako će se transakcije brže izvoditi. To se može postići s postavljanjem varijable „innodb_flush_log_at_trx_commit“ na 2. Također se može prekinuti binarno zapisivanje u dnevnike na „slave“ serveru, opcija „innodb_locks_unsefe_for_binlog“ se postavi na 1, i postaviti „delay_key_write“ na „ALL“ za MyISAM. Ove postavke mijenjaju sigurnost za brzinu. Ipak, ako se promovira „slave“ server za „master“ server, treba postaviti te vrijednosti na sigurne postavke.

Ne duplicirati zahtjevne dijelove zapisivanja
Ponovna izrada arhitekture aplikacije i/ili optimiziranje upita je često najbolji način za držanje „slave“ servera u koraku s „master“ serverom. Treba pokušati smanjiti količinu posla koja će biti duplicirana kroz sustav. Svaki zahtjevni posao s „master“ servera se možda ponovo izvršava na svakom „slave“ serveru. Ako se može maknuti posao s „master“ servera na „slave“ server, samo jedan od „slave“ servera će morati raditi posao. Tada se rezultati zapisivanja mogu postaviti na „master“ server, na primjer, s „LOAD DATA INFILE“.
Na primjer pretpostavimo da imamo jako veliku tablicu koja radi sumiranje na manje tablice:

mysql> REPLACE INTO glavna_baza. zbrojna_tablica (col1, col2, …)
¬> SELECT col1, sum(col2, …)
¬> FROM glavna_baza.ogromna_tablica GROUP BY col1;
Ako se izvode ti upiti na „master“ serveru, svaki „slave“ server će morati ponoviti ogroman „GROUP BY“ upit. Ako se radi puno takvih upita, „slave“ serveri neće držati korak s „master“ serverom. Na „slave“ serveru, moguće na specijalnim bazama podataka (baza podataka „zbrojna_baza“), rezerviranim u svrhu izbjegavanja konflikata s podacima koji su replicirani s „master“ servera, pokrene se sljedeće:

mysql> REPLACE INTO zbrojna_baza.zbrojna_tablica (col1, col2, …)
¬> SELECT col1, sum(col2, …)
¬> FROM glavna_baza.ogromna_tablica GROUP BY col1;
Ako se izvode ti upiti na „master“ serveru, svaki „slave“ server će morati ponoviti ogroman „GROUP BY“ upit. Ako se radi puno takvih upita, „slave“ serveri neće držati korak s „master“ serverom. Na „slave“ serveru, moguće na specijalnim bazama podataka (baza podataka „zbrojna_baza“), rezerviranim u svrhu izbjegavanja konflikata s podacima koji su replicirani s „master“ servera, pokrene se sljedeće:

mysql> REPLACE INTO zbrojna_baza.zbrojna_tablica (col1, col2, …)
¬> SELECT col1, sum(col2, …)
¬> FROM glavna_baza.ogromna_tablica GROUP BY col1;
Sada se koristiti „SELECT INTO OUTFILE“, popraćeno s „LOAD DATA INFILE“ na „master“ serveru, i vrate se rezultati natrag na „master“ server. Duplicirani posao je reduciran s jednostavnom naredbom „LOAD DATA INFILE“. Ako imamo N „slave“ servera, upravo se smanjilo N¬1 ogromnih GROUP BY upita.

Problem s ovom strategijom je poslovanje sa zagušljivim podacima. Ponekad je teško dobiti konzistentan rezultat sa čitanjem na „slave“ serveru i zapisivanjem na „master“ serveru. Ako je teško napraviti čitanje na „slave“ serveru, tada postoji još jedan način za smanjiti količinu posla „slave“ servera. To se napravi tako što se odvoji „REPLACE“ i „SELECT“ dio upita, dohvatiti se rezultat u aplikaciju i tada umetnute ponovo natrag na „master“ server. Prvo se izvodi sljedeći upit na „master“ serveru:

mysql> SELECT col1, sum(col2, …) glavna_baza.ogromna_tablica GROUP BY col1;

Tada se rezultat može ponovo umetnuti u zbrojnu tablicu s ponavljanjem sljedećeg upita za svaki red u skupu rezultata:

mysql> REPLACE INTO glavna_baza. zbrojna_tablica (col1, col2, …) VALUES (?, ?, …);

Ponovo, poštedjelo se „slave“ servere od velikih „GROUP BY“ upita; odvajanjem „SELECT“¬a iz „REPLACE“¬a se „ SELECT“ dio upita nije replicirao na svakom „slave“ serveru.

Ova opća strategija, poštede „slave“ servera od zahtjevnih zapisivanja može pomoći u mnogim slučajevima gdje imamo upite čiji rezultat je zahtjevan za računanje, ali jednostavan za rukovanje jednom kada je izračunat.

Paralelno zapisivanje izvan replikacije
Drugi način za izbjegavanje pretjeranog kašnjenja na „slave“ serverima je zaobilaženje replikacije. Je li potrebno sve zapise s „master“ servera replicirati na „slave“ server? Kako se mogu rezervirati ograničeni kapaciteti zapisivanja „slave“ servera za zapisivanja koja stvarno trebaju biti napravljena preko replikacije?

Ako se mogu identificirati neka zapisivanja koja su jednostavna za napraviti izvan replikacije, tada se ne mora koristiti replikacija za ta zapisivanja koja bi zahtijevala velike kapacitet zapisivanja na „slave“ serveru.

Jedan odličan primjer je ako imamo veliki broj jednostavnih upita koji rade promjene na jednom retku (npr. milijun inserta), tada ne bi trebalo biti razloga za repliciranje ovih zapisivanja na „slave“ serverima. Umjesto toga, može se prekinuti binarno zapisivanje u dnevnike, i tada pokrenuti odvojeni, ali identičan proces na „master“ i „slave“ serverima.

Podijelite članak:

Pretplatite se na naš newsletter