Rýchle nakonfigurovanie MySQL master-master replikácie

Master-master replikácia MySQL databázy je super vec. Ak však objem dát presiahne istú hranicu, býva vytvorenie repliky klasickou cestou dump –> import zdĺhavé. Avšak pri zachovaní istého postupu je možné vytvorenie repliky značne urýchliť. Poďme sa pozrieť na to, ako to spraviť.

Navigácia

  1. Prvotná synchronizácia
  2. Konfiguračné súbory
  3. Granty pre replikačných užívateľov
  4. Kontrola konfiguračných súborov
  5. Sekundárna synchronizácia
  6. Čistenie nepotrebných dát
  7. Spustenie replikácie
  8. Kontrola replikácie

1. Prvotná synchronizácia

Aby sme neskôr získali čas, môžeme spraviť prvotnú synchronizáciu MySQL dát na sekundárnu stranu. Zatiaľ nemusíme MySQL na primárnej strane stopovať, kľudne ho nechajme bežať. Prenos súborov spravíme cez výborný nástroj rsync, takže na príkazovom riadku na sekundárnej strane pustíme toto:

[root@chaos ~] #rsync -a -e ssh --progress root@mini:/var/lib/mysql/ /var/lib/mysql/

Všimnite si, že zatiaľ som nepoužil prepínač –delete. To pre prípad, že by sme sa preklepli a pustili rsync opačne. V tom prípade by sme s veľkou pravdepodobnsťou prišli o dáta.

Takže údaje sa nám pekne prenášajú a v závislosti od objemu to bude asi chvíľku trvať. Verte však, že takýto prenos údajov je oveľa rýchlejší ako dump a následný import. Niekto by mohol namietať, že môže dôjsť k poškodeniu údajov v databáze ale verte mi – nedôjde. Za predpokladu, že dodržíte tuto napísaný postup.

Ďalšiu vec, ktorú budeme potrebovať k replikácii sú konfiguračné súbory. Na Debiane nájdeme tieto súbory v adresári /etc/mysql/, takže aj tie prenesieme na sekundárnu stranu. Tento príkaz na synchronizáciu však stačí spustiť iba raz, lebo niektoré parametre musia byť na jednotlivých stranách rozdielne.

[root@chaos ~] #rsync -a -e ssh --progress --delete root@mini:/etc/mysql/ /etc/mysql/

Teraz nakonfigurujeme MySQL server. Ak robíte prvotnú konfiguráciu servera, vytvorte nasledovné súbory najprv na jednej strane. Po prenose na druhú stranu hore spomínaným príkazom rsync už iba zmeníte pár parametrov špecifických pre druhý nód.

2. Konfiguračné súbory

Zoznam konfiguračných súborov (v abecednom poradí):

/etc/mysql/conf.d/charsets-utf8.cnf
Ak netušíte, čo znamená znaková sada, odporúčam vám nastaviť kódovanie ako je uvedené nižšie. Ak by ste mali pokušenie nastaviť kódovanie na cp-1250, tak vás varujem. UTF-8 je univerzálne kódovanie a vyhnete sa problémom v budúcnosti.

[mysql]
default-character-set = utf8

[mysqld]
#character_set_client            = utf8
#character_set_connection        = utf8
#character_set_database          = utf8
#character_set_results           = utf8
character_set_server            = utf8

# vim: ft=conf

/etc/mysql/conf.d/innodb.cnf
Určite si nezabudnite zapnúť InnoDB engine v MySQL. Niektoré aplikácie to doslova vyžadujú. Do pozornosti dávam parameter innodb_file_per_table. Ten zabezpečí, že vaše ohromné objemy dát nebudú v jednom InnoDB súbore /var/lib/mysql/ibdata1, ale každá tabuľka bude mať samostatný súbor. Hodí sa to minimálne vtedy, ak budete chcieť vytvárať repliku podľa tohto návodu – rsync dát zbehne rýchlejšie. A že do InnoDB ide vložiť naozaj dostatočné množstvo údajov dokazuje tento výpis:

[root@mini ~] # ls -lah /var/lib/mysql/zabbix_mini/history.ibd 
-rw-rw---- 1 mysql mysql 2.7G 2012-10-25 16:04 /var/lib/mysql/zabbix_mini/history.ibd

(root@mini) [zabbix_mini]> show table status like 'history'\G
----------------------------------- 1. row ------------------------------------
           Name: history
         Engine: InnoDB
        Version: 10
     Row_format: Compact
           Rows: 35125726
 Avg_row_length: 53
    Data_length: 1862270976
Max_data_length: 0
   Index_length: 956301312
      Data_free: 4194304
 Auto_increment: NULL
    Create_time: 2012-10-24 17:13:32
    Update_time: NULL
     Check_time: NULL
      Collation: utf8_general_ci
       Checksum: NULL
 Create_options: 
        Comment: 
1 row in set (0.34 sec)

35 miliónov záznamov v jednej tabuľke, 1.8G dát, 950MB index. A teraz už obsah konfiguračného súboru /etc/mysql/conf.d/innodb.cnf:

[mysqld]
# You might want to disable InnoDB to shrink the mysqld process by circa 100MB.
#skip-innodb

default-storage-engine = InnoDB
innodb_flush_log_at_trx_commit=2
sync_binlog=1
innodb_file_per_table=1
innodb_buffer_pool_size = 256M

# vim: ft=conf

/etc/mysql/conf.d/logging.cnf
Aby sme vedeli údaje replikovať medzi dvoma nódmi, musíme na serveri povoliť binárny log. Štandardný query-log povoľovať nemusíme, je to menší performance killer. A aby nám logy mysql nepretekali, nastavíme ich automaticḱú expiráciu (odmazávanie) po 1 dni, prípadne si tento limit môžete zvýśiť.

Nezabudnite na jeden z najdôležitejších parametrov server-id! Nastavte tento parameter na našich nódoch rozdielne. Použite napr. posledné číslo z IP adresy servera. Pre istotu skontrolujte nastavenie aj viac krát. V opačnom prípade vám replikácia nebude fungovať správne. Pravdepodobne sa vám zmeny v tabuľkách nebudú prenášať z jedného servera na druhý a kým si to všimnete, o dáta prídete.

Obdobne ja nastavujem aj relay-log rozdielne. Do názvu logu vkladám názov servera.

[mysqld]
#log             = /var/log/mysql/mysql.log
log_bin                 = /var/log/mysql/mysql-bin.log
log-error       = /var/log/mysql/mysql.err
log-warnings=2
expire_logs_days       = 1

slow-query-log-file     = /var/log/mysql/mysql-slow.log
long_query_time = 1
#log-queries-not-using-indexes

# must be different for every master node - e.g. use the last number of IP address
server-id               = 1

##binlog-do-db=
#binlog-ignore-db=mysql
#binlog-ignore-db=test

relay-log               = /var/log/mysql/mysql-relay-mini.log

# vim: ft=conf

/etc/mysql/conf.d/mysqld_safe_syslog.cnf

[mysqld_safe]
syslog

/etc/mysql/conf.d/prompt.cnf
Veľmi užitočné nastavenie MySQL – sprehľadňuje prácu cez textového mysql klienta.

[mysql]
prompt=(\\u@\\h) [\\d]>\\_

/etc/mysql/conf.d/replication.cnf
Konečne sme sa prepracovali k najdôležitejšiemu konfiguračnému súboru. V tomto súbore nesmieme zabudnúť nastaviť parameter auto-increment-offset na rozdielne hodnoty. „1“ na prvom nóde, „2“ na druhom nóde. Tým si pripravíme náš cluster pre potencionálne aplikácie, ktoré by sme chceli bežať v našom cloude a nakonfigurovali by sme ich pre zápis na oba nódy. 🙂 Parameter auto-increment-increment vlastne udáva počet nódov nášho clustera, takže nastavíme na hodnotu 2 na oboch nódoch.

Ostatné zakomentované parametre sa používali v starších verziách MySQL, ale už nie sú potrebné. Od istej verzie si MySQL slave server drží informáciu o master serveri v súbore /var/lib/mysql/master.info. Ale k tomuto súboru sa ešte dostaneme.

[mysqld]
# change to server hostname
report-host=mini

replicate-same-server-id = 0
auto-increment-increment = 2
# offset: use "1" on first node, use "2" on second node
auto-increment-offset = 2

#master-host=192.168.1.35
#master-user=repl2mini
#master-password=******very_secure_password******
#master-connect-retry=10 # num of seconds, default is 60

# Error: 1146 SQLSTATE: 42S02 (ER_NO_SUCH_TABLE)
# Error: 1149 SQLSTATE: 42000  (ER_SYNTAX_ERROR)
#slave-skip-erros=1146,1149
#slave-skip-errors=1062

# vim: ft=conf

/etc/mysql/conf.d/security.cnf
Security – komentár asi nie je veľmi potrebný. Snáď iba skontrolovať nastavenie parametra old_passwords, aby mal hodnotu false.

[mysqld]
secure_auth=1
local-infile=0
#secure_file_priv = /home/mysql-import
old_passwords = false
skip-show-database

/etc/mysql/my.cnf
Nezaoberali sme sa ešte hlavným konfiguračným súborom /etc/mysql/my.cnf. Tuningovými vecami sa teraz nebudeme zaoberať, takže iba skontrolujeme nastavenie sieťovania, aby sa na náš server vedeli pripojiť druhý nód. Na oboch nódoch teda musíme mať teda takéto riadky:

# Instead of skip-networking the default is now to listen only on
# localhost which is more compatible and is not less secure.
#bind-address       = 127.0.0.1

Áno, bind-address zakomentujeme, aby nám MySQL server počúval na každom sieťovom rozhraní na porte 3306. Ináč sa replika nebude vedieť pripojiť.

3. Granty pre replikačných užívateľov

Kým sme sa dopracovali do tohto bodu, rsync nám preniesol prvú iniciálnu kópiu databázových dát. MySQL server na prvom serveri sme zatiaľ mali pustený, aby sme nemali dlhý výpadok. Kým ho však na chvíľu odstavíme, vytvoríme ešte replikačných užívateľov. Ja používam jedného užívateľa jedným smerom a druhého druhým smerom, aby to bolo trošku prehľadnejšie.

Pripojíme sa teda do databázy a spravíme teda nasledovné granty:

[root@mini /etc/mysql]# mysql -p
Enter password: ***your-root-password***
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1229
Server version: 5.5.24-9-log (Debian)

Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

(root@mini) [(none)]> use mysql
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
(root@mini) [mysql]> SHOW GRANTS FOR repl2mini@'192.168.0.1';
+--------------------------------------------------------------------------------------------------------+
| Grants for repl2mini@192.168.0.1                                                                       |
+--------------------------------------------------------------------------------------------------------+
| GRANT REPLICATION SLAVE ON *.* TO 'repl2mini'@'192.168.0.1' IDENTIFIED BY PASSWORD '*F0172F232440F...'   |
+--------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

(root@mini) [mysql]> SHOW GRANTS FOR repl2chaos@'192.168.0.35';
+----------------------------------------------------------------------------------------------------------+
| Grants for repl2chaos@192.168.0.35                                                                       |
+----------------------------------------------------------------------------------------------------------+
| GRANT REPLICATION SLAVE ON *.* TO 'repl2chaos'@'192.168.0.35' IDENTIFIED BY PASSWORD '*F0172F232440F...'   |
+----------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

Ak neviete ako vytvoriť replikačného užívateľa, postupujte podľa oficiálnej dokumentácie k MySQL. Vygenerujte si dostatočne bezpečné heslo alebo si nainštalujte generátor hesiel apg:

$ sudo apt-get install apg
$ apg -M NCS -m 15 -x 20 -n 20
id7sweOfJejinOkof!
Vorf&FlothVeigh2
Esfat&ocnuOthPyicec1
as1orbyepnujebTesp|
wuWyHyecocs<or7

Vyberiete si teda heslo, ktoré sa vám páči a vytvoríte 2 užívateľov v databáze:

(root@mini) [mysql]> GRANT REPLICATION SLAVE ON *.* TO 'repl2mini'@'192.168.0.1' IDENTIFIED BY 'as1orbyepnujebTesp';
(root@mini) [mysql]> GRANT REPLICATION SLAVE ON *.* TO 'repl2chaos'@'192.168.0.35' IDENTIFIED BY 'as1orbyepnujebTesp';
(root@mini) [mysql]> FLUSH PRIVILEGES;

4. Kontrola konfiguračných súborov

V tejto chvíli máte teda pripravenú konfiguráciu oboch nódov. Pôvodný MySQL server sme ešte stále nechali bežať kvôli dostupnosti, ale teraz už spravíme odstávku. Finálne vysynchronizujeme databázove dáta, vyčístíme nepotrebné dáta a spustíme replikáciu.

Najprv ešte kontrola parametrov – pustíme na oboch nódoch a porovnáme výstup. S výnimkou parametrov replicate-same-server-id a bind-address musia byť parametre na jednotlivých nódoch rozdielne:

[root@mini ~] #for par in report-host auto-increment-offset server-id bind-address relay-log; do \
    grep -r $par /etc/mysql/; \
done
/etc/mysql/conf.d/replication.cnf:report-host=mini
/etc/mysql/conf.d/replication.cnf:auto-increment-offset = 2
/etc/mysql/conf.d/logging.cnf:server-id               = 1
/etc/mysql/conf.d/replication.cnf:replicate-same-server-id = 0
/etc/mysql/my.cnf:#bind-address         = 127.0.0.1
/etc/mysql/conf.d/logging.cnf:relay-log         = /var/log/mysql/mysql-relay-mini.log

[root@chaos ~] #for par in report-host auto-increment-offset server-id bind-address relay-log; do \
    grep -r $par /etc/mysql/; \
done
/etc/mysql/conf.d/replication.cnf:report-host=chaos
/etc/mysql/conf.d/replication.cnf:auto-increment-offset = 2
/etc/mysql/conf.d/logging.cnf:server-id               = 35
/etc/mysql/conf.d/replication.cnf:replicate-same-server-id = 0
/etc/mysql/my.cnf:#bind-address         = 127.0.0.1
/etc/mysql/conf.d/logging.cnf:relay-log         = /var/log/mysql/mysql-relay-chaos.log

5. Sekundárna synchronizácia

Vypneme MySQL server na prvom nóde (a pre istotu aj na druhom, aj keď tam ešte nemal byť spustený) a opäť vysynchronizujeme dáta. Tentoraz by už synchronizácia mala prebehnúť oveľa rýchlejšie, záleží na tom, koľko dát sa zapisovalo počas prvotnej synchronizácie.

[root@mini  ~] #/etc/init.d/mysql stop
[root@chaos ~] #/etc/init.d/mysql stop

V synchronizácii už aj pridáme parameter –delete. Až do štartu MySQl servera sa snažíme pracovať rýchlo – minimalizujeme čas odstávky. Ideálne je najprv si pripraviť príslušné príkazy do shellu a odklepnúť ich vo chvíli, keď zastavíme MySQL server.

[root@chaos ~] #rsync -a -e ssh --progress --delete root@mini:/var/lib/mysql/ /var/lib/mysql/

6. Čistenie nepotrebných dát

Počkáme na dosynchronizovanie dát a na oboch nódoch zmažeme nasledovné súbory:

[root@mini  ~] #rm -f /var/lib/mysql/master.info /var/log/mysql/*
[root@chaos ~] #rm -f /var/lib/mysql/master.info /var/log/mysql/*

Tým sme zabezpečili, že keď teraz spustíme servery, nebude naštartovaná žiadna replikácia, obaja mastri budú začínať tým istým binárnym logom na pozícii 0 a dáta budú mať identické. Ideálny stav. Štartujeme teda oba nódy (zatiaľ bez replikácie):

[root@mini  ~] #/etc/init.d/mysql start
[root@chaos ~] #/etc/init.d/mysql start

Kedže pôvodný server už máme dostupný, pravdepodobne sa na neho už pripojili naše aplikácie a začali niečo zapisovať. Mali by sme to vidieť podľa rastúcej polohy v binárnom logu:

(root@mini) [(none)]> SHOW MASTER STATUS;
+------------------+----------+--------------+------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000001 |     7268 |              |                  |
+------------------+----------+--------------+------------------+

7. Spustenie replikácie

Spustíme teda replikáciu. Na oboch nódoch teda pustíme:

(root@chaos) [mysql]> CHANGE MASTER TO MASTER_HOST='192.168.0.1', 
    MASTER_LOG_FILE ='mysql-bin.000001', MASTER_LOG_POS=0, 
    MASTER_USER = 'repl2chaos',  MASTER_PASSWORD = 'as1orbyepnujebTesp';
(root@chaos) [mysql]> START SLAVE;
(root@mini) [mysql]> CHANGE MASTER TO MASTER_HOST='192.168.0.35', 
    MASTER_LOG_FILE ='mysql-bin.000001', MASTER_LOG_POS=0, 
    MASTER_USER = 'repl2mini',  MASTER_PASSWORD = 'as1orbyepnujebTesp';
(root@mini) [mysql]> START SLAVE;

8. Kontrola replikácie

Ak sme postupovali správne, mali by sme vidieť spustenú replikáciu. Parametre Slave_IO_Running a Slave_SQL_Running by mali mať oba hodnotu Yes.

(root@mini) [(none)]> SHOW SLAVE STATUS \G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.168.0.35
                  Master_User: repl2mini
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000002
          Read_Master_Log_Pos: 107
               Relay_Log_File: mysql-relay-mini.000004
                Relay_Log_Pos: 253
        Relay_Master_Log_File: mysql-bin.000002
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB: 
          Replicate_Ignore_DB: 
           Replicate_Do_Table: 
       Replicate_Ignore_Table: 
      Replicate_Wild_Do_Table: 
  Replicate_Wild_Ignore_Table: 
                   Last_Errno: 0
                   Last_Error: 
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 107
              Relay_Log_Space: 453
              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: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error: 
               Last_SQL_Errno: 0
               Last_SQL_Error: 
  Replicate_Ignore_Server_Ids: 
             Master_Server_Id: 35
1 row in set (0.00 sec)