sql >> Databáze >  >> RDS >> MariaDB

Vícenásobná zpožděná replikace Slave pro zotavení po havárii s nízkou RTO

Zpožděná replikace umožňuje podřízenému replikačnímu zařízení záměrně zaostávat za hlavním serverem alespoň o určitou dobu. Před provedením události slave nejprve v případě potřeby počká, dokud neuplyne daný čas od vytvoření události na masteru. Výsledkem je, že slave bude odrážet stav mastera někdy v minulosti. Tato funkce je podporována od MySQL 5.6 a MariaDB 10.2.3. Může se hodit v případě náhodného smazání dat a měl by být součástí vašeho plánu obnovy po havárii.

Problémem při nastavování podřízeného zařízení se zpožděnou replikací je, jak velké zpoždění bychom měli zavést. Příliš málo času a riskujete, že se špatný dotaz dostane k vašemu zpožděnému otrokovi dříve, než se k němu dostanete, a tím ztrácíte smysl mít zpožděného otroka. Volitelně můžete nastavit, aby byl čas zpoždění tak dlouhý, že vašemu zpožděnému podřízenému zařízení bude trvat hodiny, než dohoní místo, kde byl hlavní v době chyby.

Naštěstí s Dockerem je izolace procesů jeho silnou stránkou. Spuštění více instancí MySQL je s Dockerem docela pohodlné. Umožňuje nám mít více zpožděných podřízených jednotek v rámci jednoho fyzického hostitele, abychom zkrátili dobu obnovy a ušetřili hardwarové zdroje. Pokud si myslíte, že 15minutové zpoždění je příliš krátké, můžeme mít další instanci s 1hodinovým zpožděním nebo 6hodin pro ještě starší snímek naší databáze.

V tomto blogovém příspěvku se chystáme nasadit několik zpožděných slave MySQL na jednom fyzickém hostiteli s Dockerem a ukážeme některé scénáře obnovy. Následující diagram ilustruje naši finální architekturu, kterou chceme postavit:

Naše architektura se skládá z již nasazené 2uzlové replikace MySQL běžící na fyzických serverech (modrá) a rádi bychom zřídili další tři slave MySQL (zelená) s následujícím chováním:

  • 15minutové zpoždění
  • 1 hodina zpoždění
  • 6hodinové zpoždění

Vezměte na vědomí, že budeme mít 3 kopie přesně stejných dat na stejném fyzickém serveru. Ujistěte se, že náš hostitel Docker má požadované úložiště, proto mu předem přidělte dostatek místa na disku.

Příprava MySQL Master

Nejprve se přihlaste k hlavnímu serveru a vytvořte uživatele replikace:

mysql> GRANT REPLICATION SLAVE ON *.* TO [email protected]'%' IDENTIFIED BY 'YlgSH6bLLy';

Poté vytvořte zálohu kompatibilní s PITR na hlavním serveru:

$ mysqldump -uroot -p --flush-privileges --hex-blob --opt --master-data=1 --single-transaction --skip-lock-tables --skip-lock-tables --triggers --routines --events --all-databases | gzip -6 -c > mysqldump_complete.sql.gz

Pokud používáte ClusterControl, můžete snadno vytvořit zálohu kompatibilní s PITR. Přejděte na Zálohy -> Vytvořit zálohu a v rozevíracím seznamu "Typ výpisu" vyberte "Kompletní kompatibilní s PITR":

Nakonec přeneste tuto zálohu na hostitele Docker:

$ scp mysqldump_complete.sql.gz [email protected]:~

Tento záložní soubor bude použit podřízenými kontejnery MySQL během procesu zavádění podřízených zařízení, jak je uvedeno v další části.

Zpožděné nasazení Slave

Připravte si naše adresáře kontejnerů Docker. Vytvořte 3 adresáře (mysql.conf.d, datadir a sql) pro každý kontejner MySQL, který se chystáme spustit (pro zjednodušení příkazů níže můžete použít smyčku):

$ mkdir -p /storage/mysql-slave-15m/mysql.conf.d
$ mkdir -p /storage/mysql-slave-15m/datadir
$ mkdir -p /storage/mysql-slave-15m/sql
$ mkdir -p /storage/mysql-slave-1h/mysql.conf.d
$ mkdir -p /storage/mysql-slave-1h/datadir
$ mkdir -p /storage/mysql-slave-1h/sql
$ mkdir -p /storage/mysql-slave-6h/mysql.conf.d
$ mkdir -p /storage/mysql-slave-6h/datadir
$ mkdir -p /storage/mysql-slave-6h/sql

Adresář "mysql.conf.d" bude ukládat náš vlastní konfigurační soubor MySQL a bude mapován do kontejneru pod /etc/mysql.conf.d. „datadir“ je místo, kam chceme, aby Docker uložil datový adresář MySQL, který se mapuje na /var/lib/mysql kontejneru a adresář „sql“ ukládá naše soubory SQL – záložní soubory ve formátu .sql nebo .sql.gz na fázi slave před replikací a také soubory .sql pro automatizaci konfigurace replikace a spouštění.

15minutový odložený slave

Připravte si konfigurační soubor MySQL pro náš 15minutový odložený slave:

$ vim /storage/mysql-slave-15m/mysql.conf.d/my.cnf

A přidejte následující řádky:

[mysqld]
server_id=10015
binlog_format=ROW
log_bin=binlog
log_slave_updates=1
gtid_mode=ON
enforce_gtid_consistency=1
relay_log=relay-bin
expire_logs_days=7
read_only=ON

** Hodnota server-id, kterou jsme použili pro tohoto slave zařízení, je 10015.

Dále v adresáři /storage/mysql-slave-15m/sql vytvořte dva soubory SQL, jeden pro RESET MASTER (1reset_master.sql) a druhý pro vytvoření replikačního spojení pomocí příkazu CHANGE MASTER (3setup_slave.sql).

Vytvořte textový soubor 1reset_master.sql a přidejte následující řádek:

RESET MASTER;

Vytvořte textový soubor 3setup_slave.sql a přidejte následující řádky:

CHANGE MASTER TO MASTER_HOST = '192.168.55.171', MASTER_USER = 'rpl_user', MASTER_PASSWORD = 'YlgSH6bLLy', MASTER_AUTO_POSITION = 1, MASTER_DELAY=900;
START SLAVE;

MASTER_DELAY=900 se rovná 15 minutám (v sekundách). Poté zkopírujte záložní soubor převzatý z našeho hlavního serveru (který byl přenesen do našeho hostitele Docker) do adresáře „sql“ a přejmenujte jej na 2mysqldump_complete.sql.gz:

$ cp ~/mysqldump_complete.tar.gz /storage/mysql-slave-15m/sql/2mysqldump_complete.tar.gz

Konečný vzhled našeho adresáře "sql" by měl vypadat nějak takto:

$ pwd
/storage/mysql-slave-15m/sql
$ ls -1
1reset_master.sql
2mysqldump_complete.sql.gz
3setup_slave.sql

Vezměte na vědomí, že před názvem souboru SQL dáváme předponu celé číslo, abychom určili pořadí provádění, když Docker inicializuje kontejner MySQL.

Jakmile je vše na svém místě, spusťte kontejner MySQL pro náš 15minutový odložený slave:

$ docker run -d \
--name mysql-slave-15m \
-e MYSQL_ROOT_PASSWORD=password \
--mount type=bind,source=/storage/mysql-slave-15m/datadir,target=/var/lib/mysql \
--mount type=bind,source=/storage/mysql-slave-15m/mysql.conf.d,target=/etc/mysql/mysql.conf.d \
--mount type=bind,source=/storage/mysql-slave-15m/sql,target=/docker-entrypoint-initdb.d \
mysql:5.7

** Hodnota MYSQL_ROOT_PASSWORD musí být stejná jako kořenové heslo MySQL na hlavním serveru.

Následující řádky jsou tím, co hledáme, abychom si ověřili, zda MySQL běží správně a je připojeno jako slave k našemu masteru (192.168.55.171):

$ docker logs -f mysql-slave-15m
...
2018-12-04T04:05:24.890244Z 0 [Note] mysqld: ready for connections.
Version: '5.7.24-log'  socket: '/var/run/mysqld/mysqld.sock'  port: 3306  MySQL Community Server (GPL)
2018-12-04T04:05:25.010032Z 2 [Note] Slave I/O thread for channel '': connected to master '[email protected]:3306',replication started in log 'FIRST' at position 4

Poté můžete ověřit stav replikace pomocí následujícího příkazu:

$ docker exec -it mysql-slave-15m mysql -uroot -p -e 'show slave status\G'
...
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
                    SQL_Delay: 900
                Auto_Position: 1
...

V tuto chvíli se náš 15minutový zpožděný slave kontejner správně replikuje a naše architektura vypadá asi takto:

1hodinová zpožděná podřízená jednotka

Připravte si konfigurační soubor MySQL pro našeho 1-hodinového zpožděného slave:

$ vim /storage/mysql-slave-1h/mysql.conf.d/my.cnf

A přidejte následující řádky:

[mysqld]
server_id=10060
binlog_format=ROW
log_bin=binlog
log_slave_updates=1
gtid_mode=ON
enforce_gtid_consistency=1
relay_log=relay-bin
expire_logs_days=7
read_only=ON

** Hodnota server-id, kterou jsme použili pro tohoto slave zařízení, je 10060.

Dále v adresáři /storage/mysql-slave-1h/sql vytvořte dva soubory SQL, jeden pro RESET MASTER (1reset_master.sql) a druhý pro vytvoření replikačního spojení pomocí příkazu CHANGE MASTER (3setup_slave.sql).

Vytvořte textový soubor 1reset_master.sql a přidejte následující řádek:

RESET MASTER;

Vytvořte textový soubor 3setup_slave.sql a přidejte následující řádky:

CHANGE MASTER TO MASTER_HOST = '192.168.55.171', MASTER_USER = 'rpl_user', MASTER_PASSWORD = 'YlgSH6bLLy', MASTER_AUTO_POSITION = 1, MASTER_DELAY=3600;
START SLAVE;

MASTER_DELAY=3600 se rovná 1 hodině (v sekundách). Poté zkopírujte záložní soubor převzatý z našeho hlavního serveru (který byl přenesen do našeho hostitele Docker) do adresáře „sql“ a přejmenujte jej na 2mysqldump_complete.sql.gz:

$ cp ~/mysqldump_complete.tar.gz /storage/mysql-slave-1h/sql/2mysqldump_complete.tar.gz

Konečný vzhled našeho adresáře "sql" by měl vypadat nějak takto:

$ pwd
/storage/mysql-slave-1h/sql
$ ls -1
1reset_master.sql
2mysqldump_complete.sql.gz
3setup_slave.sql

Vezměte na vědomí, že před názvem souboru SQL dáváme předponu celé číslo, abychom určili pořadí provádění, když Docker inicializuje kontejner MySQL.

Jakmile je vše na svém místě, spusťte kontejner MySQL pro naši 1 hodinu zpožděnou podřízenou službu:

$ docker run -d \
--name mysql-slave-1h \
-e MYSQL_ROOT_PASSWORD=password \
--mount type=bind,source=/storage/mysql-slave-1h/datadir,target=/var/lib/mysql \
--mount type=bind,source=/storage/mysql-slave-1h/mysql.conf.d,target=/etc/mysql/mysql.conf.d \
--mount type=bind,source=/storage/mysql-slave-1h/sql,target=/docker-entrypoint-initdb.d \
mysql:5.7

** Hodnota MYSQL_ROOT_PASSWORD musí být stejná jako kořenové heslo MySQL na hlavním serveru.

Následující řádky jsou tím, co hledáme, abychom si ověřili, zda MySQL běží správně a je připojeno jako slave k našemu masteru (192.168.55.171):

$ docker logs -f mysql-slave-1h
...
2018-12-04T04:05:24.890244Z 0 [Note] mysqld: ready for connections.
Version: '5.7.24-log'  socket: '/var/run/mysqld/mysqld.sock'  port: 3306  MySQL Community Server (GPL)
2018-12-04T04:05:25.010032Z 2 [Note] Slave I/O thread for channel '': connected to master '[email protected]:3306',replication started in log 'FIRST' at position 4

Poté můžete ověřit stav replikace pomocí následujícího příkazu:

$ docker exec -it mysql-slave-1h mysql -uroot -p -e 'show slave status\G'
...
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
                    SQL_Delay: 3600
                Auto_Position: 1
...

V tuto chvíli se naše 15minutové a 1hodinové zpožděné podřízené kontejnery MySQL replikují z hlavního serveru a naše architektura vypadá asi takto:

6hodinová zpožděná podřízená jednotka

Připravte si konfigurační soubor MySQL pro našeho 6hodinového zpožděného slave:

$ vim /storage/mysql-slave-15m/mysql.conf.d/my.cnf

A přidejte následující řádky:

[mysqld]
server_id=10006
binlog_format=ROW
log_bin=binlog
log_slave_updates=1
gtid_mode=ON
enforce_gtid_consistency=1
relay_log=relay-bin
expire_logs_days=7
read_only=ON

** Hodnota server-id, kterou jsme použili pro tohoto slave zařízení, je 10006.

Dále v adresáři /storage/mysql-slave-6h/sql vytvořte dva soubory SQL, jeden pro RESET MASTER (1reset_master.sql) a druhý pro vytvoření replikačního spojení pomocí příkazu CHANGE MASTER (3setup_slave.sql).

Vytvořte textový soubor 1reset_master.sql a přidejte následující řádek:

RESET MASTER;

Vytvořte textový soubor 3setup_slave.sql a přidejte následující řádky:

CHANGE MASTER TO MASTER_HOST = '192.168.55.171', MASTER_USER = 'rpl_user', MASTER_PASSWORD = 'YlgSH6bLLy', MASTER_AUTO_POSITION = 1, MASTER_DELAY=21600;
START SLAVE;

MASTER_DELAY=21600 se rovná 6 hodinám (v sekundách). Poté zkopírujte záložní soubor převzatý z našeho hlavního serveru (který byl přenesen do našeho hostitele Docker) do adresáře „sql“ a přejmenujte jej na 2mysqldump_complete.sql.gz:

$ cp ~/mysqldump_complete.tar.gz /storage/mysql-slave-6h/sql/2mysqldump_complete.tar.gz

Konečný vzhled našeho adresáře "sql" by měl vypadat nějak takto:

$ pwd
/storage/mysql-slave-6h/sql
$ ls -1
1reset_master.sql
2mysqldump_complete.sql.gz
3setup_slave.sql

Vezměte na vědomí, že před názvem souboru SQL dáváme předponu celé číslo, abychom určili pořadí provádění, když Docker inicializuje kontejner MySQL.

Jakmile je vše na svém místě, spusťte kontejner MySQL pro náš 6hodinový odložený slave:

$ docker run -d \
--name mysql-slave-6h \
-e MYSQL_ROOT_PASSWORD=password \
--mount type=bind,source=/storage/mysql-slave-6h/datadir,target=/var/lib/mysql \
--mount type=bind,source=/storage/mysql-slave-6h/mysql.conf.d,target=/etc/mysql/mysql.conf.d \
--mount type=bind,source=/storage/mysql-slave-6h/sql,target=/docker-entrypoint-initdb.d \
mysql:5.7

** Hodnota MYSQL_ROOT_PASSWORD musí být stejná jako kořenové heslo MySQL na hlavním serveru.

Následující řádky jsou tím, co hledáme, abychom si ověřili, zda MySQL běží správně a je připojeno jako slave k našemu masteru (192.168.55.171):

$ docker logs -f mysql-slave-6h
...
2018-12-04T04:05:24.890244Z 0 [Note] mysqld: ready for connections.
Version: '5.7.24-log'  socket: '/var/run/mysqld/mysqld.sock'  port: 3306  MySQL Community Server (GPL)
2018-12-04T04:05:25.010032Z 2 [Note] Slave I/O thread for channel '': connected to master '[email protected]:3306',replication started in log 'FIRST' at position 4

Poté můžete ověřit stav replikace pomocí následujícího příkazu:

$ docker exec -it mysql-slave-6h mysql -uroot -p -e 'show slave status\G'
...
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
                    SQL_Delay: 21600
                Auto_Position: 1
...

V tuto chvíli se naše 5minutové, 1hodinové a 6hodinové zpožděné slave kontejnery správně replikují a naše architektura vypadá asi takto:

Scénář obnovy po havárii

Řekněme, že uživatel omylem upustil špatný sloupec na velký stůl. Předpokládejme, že na hlavním serveru byl proveden následující příkaz:

mysql> USE shop;
mysql> ALTER TABLE settings DROP COLUMN status;

Pokud budete mít to štěstí, že si to uvědomíte okamžitě, můžete použít 15minutové zpožděné slave, abyste dohnali okamžik před katastrofou a povýšili jej na master, nebo exportujte chybějící data a obnovte je na master.

Za prvé, musíme najít pozici binárního logu před tím, než došlo ke katastrofě. Získejte čas nyní() na hlavním:

mysql> SELECT now();
+---------------------+
| now()               |
+---------------------+
| 2018-12-04 14:55:41 |
+---------------------+

Poté získejte aktivní binární soubor protokolu na hlavním serveru:

mysql> SHOW MASTER STATUS;
+---------------+----------+--------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| File          | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set                                                                                                                                                                     |
+---------------+----------+--------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| binlog.000004 | 20260658 |              |                  | 1560665e-ed2b-11e8-93fa-000c29b7f985:1-12031,
1b235f7a-d37b-11e8-9c3e-000c29bafe8f:1-62519,
1d8dc60a-e817-11e8-82ff-000c29bafe8f:1-326575,
791748b3-d37a-11e8-b03a-000c29b7f985:1-374 |
+---------------+----------+--------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

Pomocí stejného formátu data extrahujte požadované informace z binárního protokolu binlog.000004. Odhadujeme čas zahájení čtení z binlogu asi před 20 minutami (4. 12. 2018 14:35:00) a filtrujeme výstup tak, aby zobrazoval 25 řádků před příkazem „drop column“:

$ mysqlbinlog --start-datetime="2018-12-04 14:35:00" --stop-datetime="2018-12-04 14:55:41" /var/lib/mysql/binlog.000004 | grep -i -B 25 "drop column"
'/*!*/;
# at 19379172
#181204 14:54:45 server id 1  end_log_pos 19379232 CRC32 0x0716e7a2     Table_map: `shop`.`settings` mapped to number 766
# at 19379232
#181204 14:54:45 server id 1  end_log_pos 19379460 CRC32 0xa6187edd     Write_rows: table id 766 flags: STMT_END_F

BINLOG '
tSQGXBMBAAAAPAAAACC0JwEAAP4CAAAAAAEABnNidGVzdAAHc2J0ZXN0MgAFAwP+/gME/nj+PBCi
5xYH
tSQGXB4BAAAA5AAAAAS1JwEAAP4CAAAAAAEAAgAF/+AYwwAAysYAAHc0ODYyMjI0NjI5OC0zNDE2
OTY3MjY5OS02MDQ1NTQwOTY1Ny01MjY2MDQ0MDcwOC05NDA0NzQzOTUwMS00OTA2MTAxNzgwNC05
OTIyMzM3NzEwOS05NzIwMzc5NTA4OC0yODAzOTU2NjQ2MC0zNzY0ODg3MTYzOTswMTM0MjAwNTcw
Ni02Mjk1ODMzMzExNi00NzQ1MjMxODA1OS0zODk4MDQwMjk5MS03OTc4MTA3OTkwNQEAAADdfhim
'/*!*/;
# at 19379460
#181204 14:54:45 server id 1  end_log_pos 19379491 CRC32 0x71f00e63     Xid = 622405
COMMIT/*!*/;
# at 19379491
#181204 14:54:46 server id 1  end_log_pos 19379556 CRC32 0x62b78c9e     GTID    last_committed=11507    sequence_number=11508   rbr_only=no
SET @@SESSION.GTID_NEXT= '1560665e-ed2b-11e8-93fa-000c29b7f985:11508'/*!*/;
# at 19379556
#181204 14:54:46 server id 1  end_log_pos 19379672 CRC32 0xc222542a     Query   thread_id=3162  exec_time=1     error_code=0
SET TIMESTAMP=1543906486/*!*/;
/*!\C utf8 *//*!*/;
SET @@session.character_set_client=33,@@session.collation_connection=33,@@session.collation_server=8/*!*/;
ALTER TABLE settings DROP COLUMN status

V několika spodních řádcích výstupu mysqlbinlog byste měli mít chybný příkaz, který byl proveden na pozici 19379556. Pozice, kterou bychom měli obnovit, je jeden krok před touto, což je pozice 19379491. Toto je pozice binlogu, kde chceme zpožděný otrok být až až.

Poté na vybraném zpožděném slave zařízení zastavte slave zařízení se zpožděnou replikací a znovu spusťte slave do pevné koncové polohy, kterou jsme zjistili výše:

$ docker exec -it mysql-slave-15m mysql -uroot -p
mysql> STOP SLAVE;
mysql> START SLAVE UNTIL MASTER_LOG_FILE = 'binlog.000004', MASTER_LOG_POS = 19379491;

Sledujte stav replikace a počkejte, dokud se Exec_Master_Log_Pos nebude rovnat hodnotě Until_Log_Pos. To může chvíli trvat. Po zachycení byste měli vidět následující:

$ docker exec -it mysql-slave-15m mysql -uroot -p -e 'SHOW SLAVE STATUS\G'
... 
          Exec_Master_Log_Pos: 19379491
              Relay_Log_Space: 50552186
              Until_Condition: Master
               Until_Log_File: binlog.000004
                Until_Log_Pos: 19379491
...

Nakonec ověřte, zda jsou tam chybějící údaje, které jsme hledali (sloupec "stav" stále existuje):

mysql> DESCRIBE shop.settings;
+--------+------------------+------+-----+---------+----------------+
| Field  | Type             | Null | Key | Default | Extra          |
+--------+------------------+------+-----+---------+----------------+
| id     | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| sid    | int(10) unsigned | NO   | MUL | 0       |                |
| param  | varchar(100)     | NO   |     |         |                |
| value  | varchar(255)     | NO   |     |         |                |
| status | int(11)          | YES  |     | 1       |                |
+--------+------------------+------+-----+---------+----------------+

Poté exportujte tabulku z našeho slave kontejneru a přeneste ji na hlavní server:

$ docker exec -it mysql-slave-1h mysqldump -uroot -ppassword --single-transaction shop settings > shop_settings.sql

Odstraňte problematickou tabulku a obnovte ji zpět na hlavní:

$ mysql -uroot -p -e 'DROP TABLE shop.settings'
$ mysqldump -uroot -p -e shop < shop_setttings.sql

Nyní jsme obnovili náš stůl zpět do původního stavu před katastrofální událostí. Abychom to shrnuli, zpožděnou replikaci lze použít k několika účelům:

  • Pro ochranu před chybami uživatelů na hlavním serveru. DBA může vrátit zpožděného otroka do doby těsně před katastrofou.
  • Pro testování, jak se systém chová, když dojde ke zpoždění. Například v aplikaci může být zpoždění způsobeno velkým zatížením podřízeného zařízení. Může však být obtížné vygenerovat tuto úroveň zatížení. Zpožděná replikace může simulovat zpoždění, aniž by bylo nutné simulovat zátěž. Lze jej také použít k ladění podmínek souvisejících se zpožděným podřízeným zařízením.
  • Aby bylo možné zkontrolovat, jak databáze vypadala v minulosti, aniž byste museli znovu načítat zálohu. Pokud je například zpoždění jeden týden a DBA potřebuje vidět, jak databáze vypadala před vývojem v posledních několika dnech, lze zpožděný slave zkontrolovat.

Poslední myšlenky

S Dockerem lze efektivně provozovat více instancí MySQL na stejném fyzickém hostiteli. Můžete použít nástroje pro orchestraci Docker, jako je Docker Compose a Swarm, ke zjednodušení nasazení více kontejnerů, na rozdíl od kroků uvedených v tomto příspěvku na blogu.


  1. správně nastavit sqlite db v Androidu

  2. SQL dotaz pro stromovou tabulku

  3. Jak změnit sekundy na časovou hodnotu v MySQL

  4. Jak nasadit aplikaci s databází serveru SQL na klientech