【AICon】 如何构建高效的 RAG 系统?RAG 技术在实际应用中遇到的挑战及应对策略?>>> 了解详情
写点什么

如何利用 PostgreSQL 的延迟复制实现灾备

  • 2019-02-18
  • 本文字数:4069 字

    阅读完需:约 13 分钟

如何利用PostgreSQL的延迟复制实现灾备

GitLab 网站的运营工作由 GitLab 基础设施团队负责,同时这也是 GitLab 目前最大的实例:拥有约 300 万用户和近 700 万个项目,是互联网上最大的单租户开源 SaaS 站点之一。


PostgreSQL 是 GitLab 网站基础设施的关键组成部分,我们采用了各种策略来提升系统弹性,抵御各种因数据丢失导致的灾难性事故。当然,事故的发生本来就是小概率事件,但我们也做好了备份和复制机制,一旦发生事故,可以从这些场景中恢复。


我们通常会存在这样一种误解:认为复制是备份数据库的一种手段。但是,在这篇文章中,我们将探讨如何通过延迟复制在意外删除数据后恢复数据:在 GitLab 网站上,用户删除了 gitlab-ce 项目的标签,与标签相关联的合并请求和问题也会丢失。


有了延迟副本,我们能够在 90 分钟内恢复数据。我们将介绍这个过程,以及延迟复制如何帮我们实现这一目标。

使用 PostgreSQL 进行时间点恢复

PostgreSQL 提供了一个内置功能,可以将数据库状态恢复到某个特定时间点,这个功能叫作时间点恢复(PITR),它所使用的机制与保持副本最新的机制是一样的:以整个数据库集群的一致快照(一种备份)为基础,再将变更序列应用数据库状态上,直到达到某个时间点。


为了将这个功能用于冷备份,我们会定期对数据库进行基础备份,并将其存储在归档中(在 GitLab,我们将归档保留 Google Cloud Storage 中)。此外,我们通过归档预写日志(WAL)来跟踪数据库状态的变化。有了这些,我们就可以执行 PITR,以便从灾难中恢复:从灾难发生之前的快照开始,应用 WAL 归档中的变更,直到达到灾难性事件发生之前。

什么是延迟复制?

延迟复制是指应用来自 WAL 的延迟变更。也就是说,一个在物理时间 X 提交的事务只能在时间 X + d 的副本上可见(d 为延迟时间)。


PostgreSQL 提供了两种方法用来设置数据库的物理副本:归档恢复和流式复制。归档恢复基本上与 PITR 一样,不同点在于它是以持续的方式进行的:我们不断从 WAL 归档中获取变更,并以持续的方式将它们应用于副本状态。而流式复制直接从上游数据库主机获取 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-e(https://github.com/wal-e/wal-e)从归档中获取 WAL 段(restore_command),并将变更的应用延迟 8 小时(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 boolrecoveryApplyDelay(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)一起记录的物理时间。我们还可以看到,延迟只被应用于提交记录,不会被应用于其他类型的记录:直接应用数据变更,但相应的提交会延迟,因此这些变更只在配置的延迟后才可见。

如何使用延迟副本来恢复数据

假设我们有一个生产数据库集群和一个具有 8 小时延迟的副本。我们如何使用它来恢复数据?让我们来看看在意外删除标签的情况下如何进行恢复。


在事件发生之后,我们马上在延迟副本上暂停归档恢复:


SELECT pg_xlog_replay_pause();
复制代码


暂停副本可以避免副本重放 DELETE 查询的危险。如果你需要更多时间进行诊断,这个操作就非常有用。


恢复方法是让延迟副本赶在 DELETE 查询发生之前。在我们的例子中,我们大致知道 DELETE 查询的物理时间。我们从 recovery.conf 中删除了 recovery_min_apply_delay,并添加了 recovery_target_time。这样可以让副本尽可能快地赶上(没有延迟),直到达到某个时间点:


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 是一种很好的做法,例如,使用 log_statements ='ddl’可以记录 DDL 语句(如 DROP TABLE)。如果我们手头有一个事务 ID,可以使用 recovery_target_xid 来重放到 DELETE 查询之前的事务。


对于延迟复本,恢复正常的方法很简单:恢复 recovery.conf 的改动,并重新启动 Postgres。过了一会儿,副本将再次显示 8 小时的延迟——为未来的灾难做好准备。

归档恢复的好处

与使用冷备份相比,延迟副本的主要好处是它消除了从归档中恢复完整快照的步骤。这可能需要数小时时间,具体取决于网络和存储速度。在我们的例子中,从归档中获取完整的约 2TB 备份大约需要五个小时。除此之外,我们必须应用 24 小时的 WAL 才能恢复到理想的状态(在最坏的情况下)。


相比冷备份,使用延迟副本的两个好处是:


  1. 无需从归档中获取完整的备份;

  2. 我们有一个 8 个小时的 WAL 固定窗口,需要重放才能赶上。


除此之外,我们还不断测试我们从 WAL 归档执行 PITR 的能力,并通过监控延迟副本的滞后来快速实现 WAL 归档损坏或其他与 WAL 相关的问题。


在我们的示例中,完成恢复需要 50 分钟时间,并转换为每小时 110GB WAL 的恢复速率(当时归档仍在 AWS S3 上)。在工作开始 90 分钟后,事故得到缓解,数据得到恢复。

总结:延迟复制什么时候有用,什么时候没有用

延迟复制可以用作从意外数据丢失中恢复数据的第一手段,并且非常适用于在配置的延迟时间内可以知道引起丢失的事件的情况。


让我们明确一点:复制不是一种备份机制。


备份和复制是两种具有不同目的的机制:冷备份对于从灾难中恢复来说很有用,例如意外的 DELETE 或 DROP TABLE 事件。在这种情况下,我们利用冷存储中的备份来恢复表或整个数据库的早期状态。另一方面,DROP TABLE 几乎可以立即复制到正在运行的集群中的所有副本——因此正常的复制对于从这个场景中恢复是没有用的。相反,复制的目的主要是保护数据库可用性,防止单个数据库出现故障,以及用于分发负载。


即使存在延迟副本,在某些情况下我们确实冷备份,并把它存储在安全的地方:数据中心故障、静默损坏或其他不可见的事件,都需要依赖冷备份。如果只进行复制,我们可能没有那么多的好运。


英文原文:https://about.gitlab.com/2019/02/13/delayed-replication-for-disaster-recovery-with-postgresql/


2019-02-18 08:003897
用户头像

发布了 731 篇内容, 共 430.5 次阅读, 收获喜欢 1995 次。

关注

评论

发布
暂无评论
发现更多内容

微警务平台搭建,智慧警务系统开发解决方案

t13823115967

智慧警务系统开发 微警务

25道mybatis面试题,不要说你不会

田维常

mybatis

小程序市场的「App Store」来了!你准备好吃“螃蟹”了吗?

蚂蚁集团移动开发平台 mPaaS

小程序生态 mPaaS appstore

软件测试中需要使用的工具

测试人生路

软件测试

jenkins实现接口自动化持续集成(python+pytest+ Allure+git)

行者AI

盘点 2020 |协作,是另外一种常态

冯文辉

领域驱动设计 DDD 协作 远程协作 盘点2020

接口自动化测试的实现

行者AI

规划算法

田维常

算法

《迅雷链精品课》第十三课:PBFT算法

迅雷链

区块链

得物App亮相QCon全球软件开发大会,分享百倍增长背后的技术力量

得物技术

效率 技术 得物 得物技术 Qcon

排查指南 | mPaaS 小程序被卡在了三个蓝点

蚂蚁集团移动开发平台 mPaaS

小程序 问题排查 mPaaS

AOFEX交易所APP系统开发|AOFEX交易所软件开发

系统开发

双循环背景下的全球供应链机遇与挑战

CECBC

供应链物流

Locust快速上手指南

行者AI

15天成功拿到阿里offer 我是如何逆袭成功?全靠“Java程序员面试笔试通关宝典”真够可以!

比伯

Java 编程 架构 面试 程序人生

Native 与 JS 的双向通信

Minar Kotonoha

我的 500 张技术配图是怎么画的?

小林coding

程序人生 画图软件

高光时刻!美团推出Spring源码进阶宝典:脑图+视频+文档

996小迁

spring 源码 架构 笔记

接口自动化传值处理

行者AI

5年Java高工经验,我是如何成功拿下滴滴D7Offer的?

Java架构追梦

Java 学习 架构 面试 滴滴

为什么要在以太坊上构建去中心化缓存层?到底要怎样做呢?

CECBC

以太坊

3面抖音犹如开挂,一周直接拿下offer,全靠这份啃了两个月「Java进阶手册」+[Java面试宝典]

编程 程序员 面试 计算机

iOS面试基础知识 (五)

iOSer

ios 面试 底层知识

浅谈 WebRTC 的 Audio 在进入 Encoder 之前的处理流程

阿里云视频云

阿里云 音视频 WebRTC 音频技术 音频

如何从危机中提炼总结,做好2020年的复盘?

CECBC

复盘 经济

XDAG技术详解1

老五

腾讯五面、快手三面已拿offer(Java岗位),分享个人面经

程序员知识圈

Java 程序员 架构 面试 编程语言

浅谈数据仓库质量管理规范

数据社

数据仓库 数据质量管理 七日更

观察者模式

soolaugust

设计模式 观察者模式 七日更

json处理

Isuodut

数字货币交易所系统开发,区块链交易所搭建

薇電13242772558

区块链 数字货币

如何利用PostgreSQL的延迟复制实现灾备_数据库_Andreas Brandl_InfoQ精选文章