我們如何通過 PostgreSQL 使用延遲複製進行災難恢復

我們如何通過 PostgreSQL 使用延遲複製進行災難恢復
複製不是備份。 或不? 以下是我們如何使用延遲複製來從意外刪除的快捷方式中恢復。

基礎設施專家 GitLab 負責工作 亞搏體育appcom.com - 自然界中最大的 GitLab 實例。 它擁有 3 萬用戶和近 7 萬個項目,是擁有專用架構的最大開源 SaaS 網站之一。 如果沒有 PostgreSQL 資料庫系統,GitLab.com 基礎架構就不會走得太遠,我們如何確保在發生故障時資料可能遺失時的容錯能力。 這樣的災難不太可能發生,但我們已經做好了充分準備,並儲備了各種備份和複製機制。

複製不是備份資料庫的一種方式(見下文)。 但現在我們將看到如何使用惰性複制快速恢復意外刪除的數據: 亞搏體育appcom.com 用戶 刪除了快捷方式 對於該項目 gitlab-ce 並失去與合併請求和任務的連接。

通過延遲副本,我們僅用了 1,5 小時就恢復了數據。 看看它是怎麼發生的。

使用 PostgreSQL 進行時間點恢復

PostgreSQL 有一個內建函數可以將資料庫的狀態還原到特定時間點。 它被稱為 時間點恢復 (PITR) 並使用使副本保持最新的相同機制:從整個資料庫叢集的可靠快照(基礎備份)開始,我們應用一系列狀態變更直到某個時間點。

為了使用此功能進行冷備份,我們定期進行基本的數據庫備份並將其存儲在存檔中(GitLab 存檔位於 谷歌云存儲)。 我們還通過歸檔預寫日誌來監視數據庫狀態的變化(預寫日誌,沃爾)。 完成所有這些後,我們可以為災難恢復執行 PITR:從故障前拍攝的快照開始,應用 WAL 存檔中的更改直至故障發生。

什麼是延遲複製?

延遲複製是延遲應用 WAL 變更。 即交易在一小時內發生 X,但它會延遲出現在副本中 d 一個小時內 X + d.

PostgreSQL 有兩種設定實體資料庫副本的方法:備份還原和流複製。 從存檔恢復,本質上像 PITR 一樣工作,但是是連續的:我們不斷地從 WAL 存檔中檢索更改並將它們應用到副本。 A 串流複製 直接從上游數據庫主機檢索WAL流。 我們更喜歡歸檔恢復 - 它更易於管理並且具有與生產集群保持同步的正常性能。

如何設置從存檔延遲恢復

恢復選項 文件中描述的 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'

使用這些參數,我們配置了具有備份恢復功能的延遲副本。 這裡用到的是 瓦勒 提取 WAL 段(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)。 它被稱為來自 主重複循環 對於 WAL 中的每個條目。

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, &microsecs);
    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 TB 備份。 然後你仍然需要應用整個每日 WAL 來恢復到所需的狀態(在最壞的情況下)。

延遲副本在兩個方面優於冷備份:

  1. 無需從存檔中刪除整個基本備份。
  2. WAL 段有一個固定的八小時窗口,必須重複。

我們也不斷檢查是否可以從 WAL 產生 PITR,並且透過監視延遲副本的滯後,我們很快就會注意到 WAL 存檔的損壞或其他問題。

在這個例子中,我們花了 50 分鐘來恢復,這意味著速度是每小時 110 GB 的 WAL 資料(存檔仍然在 AWS S3)。 總共我們在1,5小時內解決了問題並恢復了資料。

結果:延遲副本在哪裡有用(以及在哪裡沒有用)

如果您意外丟失數據並在配置的延遲內發現此問題,請使用延遲複製作為急救措施。

但請記住:複製不是備份。

備份和復制有不同的目的。 如果您不小心創建了冷備份,那麼冷備份會派上用場 DELETEDROP TABLE。 我們從冷存儲中進行備份並恢復表或整個數據庫的先前狀態。 但同時要求 DROP TABLE 幾乎立即在工作叢集上的所有副本中複製,因此普通複製在這裡沒有幫助。 當租用單一伺服器並分配負載時,複製本身可以保持資料庫可用。

即使使用延遲副本,如果資料中心發生故障、隱藏損壞或其他無法立即註意到的事件發生,我們有時確實需要在安全的地方進行冷備份。 單獨的複製在這裡是沒有用的。

注意。 上 亞搏體育appcom.com 我們目前僅在系統層級防止資料遺失,不會在使用者層級恢復資料。

來源: www.habr.com

添加評論