Рэплікацыя - не бэкап. Ці не? Вось як мы выкарыстоўвалі адкладзеную рэплікацыю для аднаўлення, выпадкова выдаліўшы цэтлікі.
Рэплікацыя - гэта вам не сродак бэкапу баз дадзеных (gitlab-ce
З адкладзенай рэплікай мы аднавілі дадзеныя ўсяго за 1,5 гадзіны. Глядзіце, як гэта было.
Аднаўленне на момант часу з PostgreSQL
У PostgreSQL ёсць убудаваная функцыя, якая аднаўляе стан базы дадзеных на пэўны момант часу. Яна называецца
Каб выкарыстоўваць гэтую функцыю для халоднага бэкапу, мы рэгулярна робім базавы бэкап базы дадзеных і захоўваем яго ў архіве (архівы GitLab жывуць у
Што такое адкладзеная рэплікацыя?
Адкладзеная рэплікацыя - гэта прымяненне змяненняў з WAL з затрымкай. Гэта значыць, транзакцыя адбылася ў гадзіну X
, але ў рэпліцы яна з'явіцца з затрымкай d
праз гадзіну X + d
.
У PostgreSQL ёсць 2 спосабы наладзіць фізічную рэпліку базы дадзеных: аднаўленне з архіва і стрымінгавая рэплікацыя.
Як наладзіць адкладзенае аднаўленне з архіва
recovery.conf
. прыклад:
standby_mode = 'on'
restore_command = '/usr/bin/envdir /etc/wal-e.d/env /opt/wal-e/bin/wal-e wal-fetch -p 4 "%f" "%p"'
recovery_min_apply_delay = '8h'
recovery_target_timeline = 'latest'
З гэтымі параметрамі мы наладзілі адкладзеную рэпліку з аднаўленнем з архіва. Тут выкарыстоўваецца restore_command
) з архіва, а змены будуць прымяняцца праз восем гадзін (recovery_min_apply_delay
). Рэпліка будзе сачыць за зменамі часовай шкалы ў архіве, напрыклад, з-за адпрацоўкі адмовы ў кластары (recovery_target_timeline
).
С recovery_min_apply_delay
можна наладзіць стрымінгавую рэплікацыю з затрымкай, але тут ёсць пара падвохаў, якія злучаны са слотамі рэплікацыі, зваротнай сувяззю гарачага рэзерву і інш. Архіў WAL дазваляе іх пазбегнуць.
Параметр recovery_min_apply_delay
з'явіўся толькі ў PostgreSQL 9.3. У папярэдніх версіях для адкладзенай рэплікацыі трэба наладзіць камбінацыю pg_xlog_replay_pause(), pg_xlog_replay_resume()
) або ўтрымліваць сегменты WAL у архіве на час затрымкі.
Як PostgreSQL гэта робіць?
Цікаўна паглядзець, як PostgreSQL рэалізуе адкладзенае ўзнаўленне. Паглядзім на recoveryApplyDelay(XlogReaderState)
static bool
recoveryApplyDelay(XLogReaderState *record)
{
uint8 xact_info;
TimestampTz xtime;
long secs;
int microsecs;
/* nothing to do if no delay configured */
if (recovery_min_apply_delay <= 0)
return false;
/* no delay is applied on a database not yet consistent */
if (!reachedConsistency)
return false;
/*
* Is it a COMMIT record?
*
* We deliberately choose not to delay aborts since they have no effect on
* MVCC. We already allow replay of records that don't have a timestamp,
* so there is already opportunity for issues caused by early conflicts on
* standbys.
*/
if (XLogRecGetRmid(record) != RM_XACT_ID)
return false;
xact_info = XLogRecGetInfo(record) & XLOG_XACT_OPMASK;
if (xact_info != XLOG_XACT_COMMIT &&
xact_info != XLOG_XACT_COMMIT_PREPARED)
return false;
if (!getRecordTimestamp(record, &xtime))
return false;
recoveryDelayUntilTime =
TimestampTzPlusMilliseconds(xtime, recovery_min_apply_delay);
/*
* Exit without arming the latch if it's already past time to apply this
* record
*/
TimestampDifference(GetCurrentTimestamp(), recoveryDelayUntilTime,
&secs, µsecs);
if (secs <= 0 && microsecs <= 0)
return false;
while (true)
{
// Shortened:
// Use WaitLatch until we reached recoveryDelayUntilTime
// and then
break;
}
return true;
}
Сутнасць у тым, што затрымка заснавана на фізічным часе, запісаным у пазнацы часу коміта транзакцыі.xtime
). Як бачна, затрымка прымяняецца толькі да комітаў і не кранае іншыя запісы — усе змены прымяняюцца напрамую, а коміт адкладаецца, так што мы ўбачым змены толькі пасля настроенай затрымкі.
Як выкарыстоўваць адкладзеную рэпліку для аднаўлення дадзеных
Дапушчальны, у нас у прадакшэне ёсць кластар базы дадзеных і рэпліка з васьмігадзіннай затрымкай. Паглядзім, як аднавіць дадзеныя на прыкладзе
Калі мы даведаліся аб праблеме, мы
SELECT pg_xlog_replay_pause();
З паўзай у нас не было рызыкі, што рэпліка паўторыць запыт DELETE
. Карысная штука, калі трэба час ва ўсім разабрацца.
Іста ў тым, што адкладзеная рэпліка павінна дайсці да моманту перад запытам. DELETE
. Мы прыкладна ведалі фізічны час выдалення. Мы выдалілі recovery_min_apply_delay
і дадалі recovery_target_time
в recovery.conf
. Так рэпліка даходзіць да патрэбнага моманту без затрымак:
recovery_target_time = '2018-10-12 09:25:00+00'
З пазнакамі часу лепш зменшыць лішняга, каб не прамахнуцца. Праўда, чым больш убавка, тым больш дадзеных губляем. Зноў жа, калі праскочым запыт DELETE
, усё зноў выдаліцца і давядзецца пачынаць зноўку (ці наогул браць халодны бэкап для PITR).
Мы перазапусцілі адкладзены асобнік Postgres, і сегменты WAL паўтараліся да пазначанага часу. Адсачыць прагрэс на гэтым этапе можна запытам:
SELECT
-- current location in WAL
pg_last_xlog_replay_location(),
-- current transaction timestamp (state of the replica)
pg_last_xact_replay_timestamp(),
-- current physical time
now(),
-- the amount of time still to be applied until recovery_target_time has been reached
'2018-10-12 09:25:00+00'::timestamptz - pg_last_xact_replay_timestamp() as delay;
Калі пазнака часу больш не мяняецца, аднаўленне завершана. Можна наладзіць дзеянне recovery_target_action
База даных прыйшла ў стан да таго злашчаснага запыту. Цяпер можна, напрыклад, экспартаваць дадзеныя. Мы экспартавалі выдаленыя дадзеныя аб цэтліку і ўсе сувязі з задачамі і мерж-рэквестамі і перанеслі іх у працоўную базу дадзеных. Калі страты маштабныя, можна проста прасунуць рэпліку і выкарыстоўваць яе як асноўную. Але тады згубяцца ўсе змены пасля моманту, да якога мы аднавіліся.
Замест пазнак часу лепш выкарыстоўваць ID транзакцый. Карысна запісваць гэтыя ID, напрыклад, для аператараў DDL (тыпу DROP TABLE
), з дапамогай log_statements = 'ddl'
. Будзь у нас ID транзакцыі, мы б узялі recovery_target_xid
і прагналі ўсё аж да транзакцыі перад запытам DELETE
.
Вярнуцца да працы вельмі проста: прыбярыце ўсе змены з recovery.conf
і перазапусціце Postgres. Хутка ў рэпліцы зноў з'явіцца васьмігадзінная затрымка, і мы гатовы да будучых непрыемнасцяў.
Перавагі для аднаўлення
З адкладзенай рэплікай замест халоднага бэкапу не даводзіцца гадзінамі аднаўляць увесь здымак з архіва. Нам, напрыклад, трэба пяць гадзін, каб дастаць увесь базавы бэкап на 2 ТБ. А потым яшчэ давядзецца прымяніць увесь сутачны WAL, каб аднавіцца да патрэбнага стану (у горшым выпадку).
Адкладзеная рэпліка лепш халоднага бэкапу па двух пунктах:
- Ня трэба даставаць увесь базавы бэкап з архіву.
- Ёсць фіксаванае васьмігадзіннае акно сегментаў WAL, якія трэба паўтарыць.
А яшчэ мы ўвесь час правяраем, ці можна зрабіць PITR з WAL, і мы бы хутка заўважылі пашкоджанні ці іншыя праблемы з архівам WAL, сочачы за адставаннем адкладзенай рэплікі.
У гэтым прыкладзе ў нас сышло 50 хвілін на аднаўленне, гэта значыць хуткасць была 110 ГБ дадзеных WAL у гадзіну (архіў тады ўсё яшчэ быў на
Вынікі: дзе спатрэбіцца адкладзеная рэпліка (а дзе не)
Выкарыстоўвайце адкладзеную рэплікацыю як сродак першай дапамогі, калі выпадкова страцілі дадзеныя і заўважылі гэтую бяду ў межах настроенай затрымкі.
Але ўлічыце: рэплікацыя - не бэкап.
У бэкапу і рэплікацыі розныя мэты. Халодны бэкап спатрэбіцца, калі вы выпадкова зрабілі DELETE
або DROP TABLE
. Мы робім бэкап з халоднага сховішча і аднаўляем папярэдні стан табліцы ці ўсёй базы дадзеных. Але пры гэтым запыт DROP TABLE
амаль маментальна прайграваецца ва ўсіх рэпліках на працоўным кластары, таму звычайная рэплікацыя тут не выратуе. Сама па сабе рэплікацыя падтрымлівае базу дадзеных даступнай, калі здаюць асобныя серверы, і размяркоўвае нагрузку.
Нават з адкладзенай рэплікай нам часам вельмі патрэбен лядоўняў бэкап у бяспечным месцы, калі раптам адбудзецца збой дата-цэнтра, утоенае пашкоджанне ці іншыя падзеі, якія адразу не заўважыш. Тут ад адной рэплікацыі карысці няма.
Заўвага. На
Крыніца: habr.com