pgcopydb: 重启操作(快照)

四月 4, 2024

摘要:本部分文档介绍pgcopydb程序中操作重启的设计。

本文包含以下部分:

  1. 介绍
  2. 避开一致性问题
  3. 一致性和并发性:Postgres 快照
  4. pgcopydb 操作的可恢复性
  5. 快照和目录(缓存失效)

介绍

pgcopydb 设计的一个重要方面在关于并发的注意事项文档部分中进行了详细介绍,它与使用许多并发工作进程来实现并行能力有关。

即使使用多个工作进程,pgcopydb 操作的一致性也很重要。在整个操作过程中,必须保证每个工作进程都使用相同的源模式和数据集。

由于 Postgres 能够导出和导入快照,因而可以实现多个 Postgres 会话的一致性。根据 Postgres 文档中有关快照同步函数的描述:

来自 PostgreSQL 文档

PostgreSQL 允许数据库会话同步它们的快照。一个快照可确定哪些数据对使用快照的事务可见。当两个或多个会话需要查看数据库中的相同内容时,需要同步快照。如果两个会话只是独立启动其事务,则始终存在在两个 START TRANSACTION 命令的执行之间有第三个事务提交的可能性,以至于一个会话可以看到该事务的效果,而另一个会话看不到。

为了解决这个问题,PostgreSQL 允许事务导出它正在使用的快照。只要导出事务保持打开状态,其他事务就可以导入其快照,从而保证它们看到的数据库视图与第一个事务看到的完全相同。但请注意,这些事务中的任何一个所做的数据库更改对其他事务都是不可见的,因为这是由未提交的事务进行的更改。因此,事务的同步只是对于已经存在的数据,但对于它们自己所做的更改,依然跟平时一样。

快照可以使用 pg_export_snapshot 函数导出,如表 9.94 所示,并使用 SET TRANSACTION 命令导入。

使用这些 Postgres API,允许 pgcopydb 实现一致的操作,即使在使用多个工作进程时也是如此。

避开一致性问题

如果可以确保在 pgcopydb 操作的整个过程中不会对源数据库进行任何写入,这意味着没有结构更改(DDL)和数据更改(DML),则不会发生一致性问题:这是因为数据库对于我们的上下文是静态的,可能是在维护窗口设置中,应用程序与源端数据库服务的连接断开了。

请注意,pgcopydb 提供了--not-consistent选项,允许在整个操作过程中避开共享快照的所有复杂性。特别是,当完全避开一致性方面的问题时,在崩溃后恢复操作甚至实现多步骤的操作都变得更容易。

当您能够在维护窗口内工作时,能让数据库与任何应用程序保持隔离,请考虑使用--not-consistent

一致性和并发性:Postgres 快照

如上所述,Postgres 提供了不同的 API 来导出和导入快照:

  1. 函数pg_export_snapshot()导出当前快照。
  2. SQL 命令SET TRANSACTION SNAPSHOT导入给定的快照。
  3. 复制协议命令CREATE_REPLICATION_SLOT允许导出其快照。

导出 Postgres 快照可以在创建复制槽时完成,也可以使用 SQL 函数pg_export_snapshot()从非复制连接中完成。这是一种非此即彼的情况,Postgres不允许混合使用这两种方法。

还要记住,在整个 pgcopydb 操作过程中必须使用单个快照,包括结构和数据的初始 COPY,以及数据更改捕获方面,以实现一致性(无数据丢失,更改数据流中无重复数据)。

为了能够在 pgcopydb 中实现多个工作进程,并在每个进程中对同一数据库(结构、数据)有一个一致的视图,pgcopydb 需要首先导出一个公共快照,然后让每个工作进程在连接到源数据库时导入该快照。

在使用--follow选项实现数据更改捕获时,还需要在初始快照和接收到的第一个更改之间不出现空隙,并且不会发送作为初始副本一部分的更改。Postgres 知道,如何通过CREATE_REPLICATION_SLOT复制命令中的快照导出工具,来提供这种保证。

因此,Postgres API 还需要使用pgcopydb snapshot --follow命令来创建 pgcopydb 复制槽,并导出复制槽快照。

在不使用--follow选项时,pgcopydb snapshot 命令简单地使用普通查询协议连接到 Postgres 源数据库,并运行命令select pg_export_snapshot(),以获取可由所有工作进程共享的快照。

此外,用于导出快照的 Postgres API 具有以下限制:

来自 PostgreSQL 文档

快照只能在导出它的事务结束之前进行导入。

这意味着,在整个 pgcopydb 初始复制操作过程中,必须保持pgcopydb snapshot命令的运行。复制客户端仅使用复制槽来确保一致性,因此,当仅运行跟随工作进程时,不再需要保留快照。

pgcopydb 操作的可恢复性

使用 pgcopydb 时要能做到操作可恢复,要面对三个完全不同的上下文。根据上下文以及前一个操作被中断的时间,使用--resume选项再次运行相同的 pgcopydb 命令,可能会正常工作,也可能会出错,因为这种情况不允许一致地恢复被中断的操作。

避开一致性问题

在使用--resume --not-consistent选项时,在尝试恢复中断的操作时,对快照的重用没有限制。

数据的一致性复制

在使用pgcopydb clone --resume时,会重用之前尝试中使用的快照。为了使 Postgres 能够再次导入该快照,导出快照的事务必须仍在源端数据库系统上运行。

  • 单个 pgcopydb 命令

    在使用pgcopydb clone时,快照保持进程是该进程树的一部分,并且该命令的任何中断(信号、Ctrl-C、崩溃)也会终止快照保持子进程,然后快照会丢失。

  • 单独的 pgcopydb snapshot 命令

    这就是为什么pgcopydb snapshot命令可以单独使用的原因。然后,主命令pgcopydb clone自动重用该快照,即使在pgcopydb clone命令中断的情况下,也可以保留快照。

  • 外部快照

    也可以使用其他命令或软件,导出和维护 pgcopydb 使用的快照,然后使用pgcopydb clone --snapshot ...分享快照给 pgcopydb。

使用 CDC 进行数据的一致性复制

在带上--follow选项进行数据更改捕获时,要一致性地恢复操作,需要符合以下情况:

  1. 数据的初始 COPY 需要还能可以访问导出的快照。

    即使使用复制协议命令导出了快照,Postgres 仍要求会话在此时保持打开。

  2. 客户端的逻辑复制与快照操作无关,快照操作是在创建复制槽时在服务器端完成的;从那里开始,客户端所要做的就是从复制槽进行同步。

快照和目录(缓存失效)

源端目录表setup登记了有关当前 pgcopydb 命令的信息。在启动时会检查信息,以避免在不同的上下文中重用数据。

登记的信息如下,还包含了快照信息。如果不匹配,请考虑使用与您的操作相关的--resume --not-consistent

以下展示了如何在 pgcopydb 本地目录缓存中,检查它维护的当前setup表的信息:

$ sqlite3 /tmp/pgcopydb/schema/source.db
sqlite> .mode line
sqlite> select * from setup;
                      id = 1
           source_pg_uri = postgres:///pagila
           target_pg_uri = postgres:///plop
                snapshot = 00000003-00000048-1
split_tables_larger_than = 0
                 filters = {"type":"SOURCE_FILTER_TYPE_NONE"}
                  plugin =
               slot_name =

源端和目标端的连接字符串仅包含 Postgres 服务器主机名、端口、数据库名称和连接角色名称。特别地,身份验证凭据不会存储在目录中。

了解更多

pgcopydb: 复制 PostgreSQL 数据库到目标服务器