3.3. 可靠性和可用性

3.3.1. 连接
3.3.2. WAL
3.3.3. 事务
3.3.4. 锁
3.3.5. 数据完整性验证
3.3.6. 备份与恢复
3.3.7. 流复制
3.3.8. 逻辑复制

3.3.1. 连接

在排查连接问题时需要检查的事项

  • 数据库服务器是否可达?

    • 使用 telnet <host> <port>

    • 使用 nc -zv <host> <port>

    • 使用 traceroute (Unix/Linux)或 tracert (Windows),指定主机和中间路由器允许的协议。

  • 主机名和端口是否正确?

  • 服务器是否在运行?

    • 使用 lt_ctl status

  • 服务器端防火墙是否允许通过该端口进行通信?

  • 客户端防火墙是否允许与服务器端口通信?

  • lt_hba.conf 是否有任何条目允许组合使用 SSL/非SSL、客户端主机、数据库和用户?

  • listen_addresses 参数是否配置为允许通过所需的 IP 地址(包括 IPv4 和/或 IPv6)连接?

  • 数据库名称、用户名和密码是否正确?

  • 用户是否有权限连接到数据库?

    • 使用 ltsql 的 \l 或 pg_database.datacl 检查权限。

  • 数据库服务器是否有足够的 CPU 和内存资源?

  • 最大连接数限制是否已达到?

    • max_connections 和 superuser_reserved_connections 参数(实例级别)

    • CREATE/ALTER DATABASE CONNECTION LIMIT(数据库级别)

    • CREATE/ALTER ROLE CONNECTION LIMIT(用户级别)

连接终止和查询取消

  • 当连接关闭时,任何未完成的事务都将回滚。

  • 终止连接(pg_terminate_backend())和取消查询(pg_cancel_backend())并不总是有效。例如,在后端进程处于不可中断部分时,它们不起作用,如等待获取轻量级锁、针对网络存储设备的读/写系统调用以及没有取消点的循环。

  • 设置适当的 statement_timeout(语句、用户、数据库、实例级别)。广泛设置较短的超时时间并不推荐,因为它会取消有意运行的长时间查询。

  • 适当设置客户端超时时间。

  • 适当设置服务器端超时时间。

    • tcp_keepalives_idle, tcp_keepalives_interval, tcp_keepalives_count

      • TCP 保持活动仅在网络连接空闲时起作用。当套接字连接正在建立或已经发送了一些数据并在等待其 ACK 时,它不起作用。

      • 实际的超时时间为 tcp_keepalives_idle + tcp_keepalives_interval * tcp_keepalives_count。

    • tcp_user_timeout

      • 设置 TCP 重传超时。相对新近在 Linux 上可用。

      • 在 TCP 保持活动无法帮助的情况下介入,即当套接字连接正在建立或已经发送了一些数据并在等待其 ACK 时。

      • 与 TCP 保持活动一起使用时会令人困惑,因为这改变了 TCP 保持活动超时的时间。安全的做法是将 tcp_user_timeout 设置为 tcp_keepalives_idle + tcp_keepalives_interval * tcp_keepalives_count。

    • authentication_timeout

    • idle_in_transaction_session_timeout

    • idle_session_timeout

    • client_connection_check_interval

连接故障转移

  • 客户端驱动程序允许在连接字符串中指定多个主机。

    • 连接超时应用于连接字符串中的每个主机。因此,如果在列表中有许多失败的主机,连接可能会花费意想不到的长时间。

  • 故障转移后恢复会话状态:会话变量、预编译语句、临时表、保持游标(使用 DECLARE CURSOR WITH HOLD 创建)、咨询锁、会话用户(使用 SET SESSION AUTHORIZATION 设置)、当前用户(使用 SET ROLE 设置)。

3.3.2. WAL

WAL 的用途

  • 用于崩溃恢复、归档恢复(时间点恢复:PITR)及复制

  • 无论事务是否提交,更新都会重新执行。

  • 不像其他流行的 DBMS,没有撤销日志(前镜像)或操作。

    • 因此,事务回滚和崩溃恢复速度快。

    • 被中止事务所做的更改保留在内存和磁盘上,但由于 MVCC 的存在,这些更改对其他事务是不可见的。

WAL 结构

  • 一系列 8 KB 的块。

  • 每个块可以包含多个 WAL 记录。同时,每个 WAL 记录可以跨越多个块。

  • 在内存和存储上的内容相同。

  • WAL 缓冲区是在内存中的一段连续的块数组。它是以循环的方式使用的。

  • 存储上的 WAL 被划分为 WAL 段文件。默认情况下,每个 WAL 段文件大小为 16 MB,可以通过 lt_initdb--wal-segsize=size 参数来配置。

写入 WAL

  • 在内存中修改共享缓冲区中的数据页,然后将更改写入 WAL 缓冲区。

  • WAL 缓冲区始终顺序写入 WAL 文件(不是随机写入)。

  • 在将共享缓冲区中的脏数据页写入磁盘之前,首先记录所有影响该数据页的最新 WAL 记录。这一规则被称为 WAL(Write Ahead Log)。

    • 每个数据页在其页头中都有一个位置(LSN),表示最新的更新记录在 WAL 中的位置。这是页 LSN。

    • LSN(日志序列号):一个无符号 8 字节整数。它代表 WAL 段、块及其在块中的偏移。

  • 如果写入 WAL 文件失败,实例将以 PANIC 消息崩溃。

  • WAL 卷可能由于以下原因而超出 max_wal_size:

    • 大量写入,如使用 COPY 加载数据。

    • 归档 WAL 文件失败。

    • wal_keep_size 的值过大。

    • 未使用的复制槽位。

  • SELECT 可以在以下情况下修改数据页并写入 WAL:

    • 获取行锁,例如 SELECT FOR UPDATE。它们会在元组头中设置 xmax,并可能更新 MultiXact 数据结构。

    • 修剪行指针和碎片整理页面。

    • 设置元组头的提示位。当启用页面校验和时,会发出 WAL。


  

3.3.3. 事务

ACID 属性解释

  • 原子性:事务回滚和数据库恢复

  • 一致性:完整性约束和触发器,如非空、检查、主键/唯一/外键约束

  • 隔离性:多版本并发控制(MVCC)和锁

  • 持久性:WAL(写前日志)

事务 ID (XID)

  • 当事务首次修改数据时,例如在 INSERT、UPDATE、DELETE 和 SELECT FOR SHARE/UPDATE 中,会被分配一个 XID。

    • XID 分配通过 XidGen 轻量级锁(LWLock)进行序列化。

    • XID 分配通常非常快,但在某些情况下可能会遇到延迟。每 32K 个事务,它会通过 SLRU 缓存分配并清零一个新的提交日志(clog)页面。这个 clog 页面的分配可能会为了页面替换而刷新一个脏页。

    • 这可能会导致响应时间的不可预测的峰值。

  • 只读事务不会分配 XID。它们不会受到分配新 XID 时的 LWLock 争用的影响。

  • XID 存储在元组头部,并且作为 xmin 和 xmax 系统列可见(SELECT xmin, xmax, * FROM mytable)。

    • xmin 是创建元组的事务的 XID(INSERT、UPDATE、COPY)。

    • xmax 是删除或锁定元组的事务的 XID(例如,DELETE、UPDATE 或 SELECT FOR SHARE/UPDATE)。

      • 删除元组(DELETE、UPDATE)。

      • 锁定元组(例如,SELECT FOR SHARE/UPDATE)。

  • 特殊的 XID 值

    • 0:无效的 XID

    • 1:引导 XID。在 lt_initdb 期间由引导处理使用。

    • 2:冻结 XID:Lightdb 的较新版本只在序列元组中使用这个值,而不是在表中使用。

MVCC:多版本并发控制

  • MVCC 的主要优点是“读取永远不会阻塞写入,写入也永远不会阻塞读取”,即对同一行的 UPDATE/DELETE 和 SELECT 不会相互阻塞。

    • 对同一行的写入会互相阻塞。

    • 在传统的基于锁的并发控制中,对同一行的读取和写入会产生冲突。

  • 其总体工作原理如下:

    • 插入或更新一行会创建该行的新版本。更新保留旧行版本供其他运行中的事务使用。(多版本)

    • 创建事务的 XID 被设置为新行版本中的 xmin 字段。

    • 新行版本仅在创建事务提交后才对创建事务可见。

    • 一旦创建事务提交,所有后续的新事务都能看到新的行版本。其他现有事务继续看到旧行版本。旧行版本现在是一个“死元组”。

    • 删除一行并不会移除行版本。而是将删除事务的 XID 设置为行版本中的 xmax 字段。

    • 被删除的行版本仅在删除事务提交之前对其删除事务不可见。

    • 一旦删除事务提交,所有后续的新事务都无法看到该行版本。其他现有事务继续看到行版本。行版本现在是一个“死元组”。

    • 最终,当没有剩余事务能看到该死元组时,VACUUM 将移除它。

  • 元组可见性的原理:

    • 每个事务使用自己的快照、提交日志(clog)以及目标元组头部的 xmin 和/或 xmax 来确定是否能够看到特定的行版本。

    • 快照是什么:

      • 在某个时间点正在运行的事务的快照。

      • 可以运行 "SELECT pg_current_snapshot();" 查看当前事务的快照。

      • 快照的文本表示形式为 xmin:xmax:xip_list。例如,10:20:10,14,15。

      • xmin:仍在活动中的最低事务 ID。所有小于 xmin 的事务 ID 要么已提交且可见,要么已回滚且无效。

      • xmax:最高完成事务 ID 的下一个 ID。所有大于等于 xmax 的事务 ID 在快照时刻尚未完成,因此不可见。

      • xip_list: 快照时正在进行的事务。xmin <= X < xmax 且不在此列表中的事务 ID 在快照时已经完成,因此根据其提交状态,要么可见,要么已死亡。

      • 在 READ COMMITTED 事务中,每次 SQL 语句开始时都会获取一个快照。

      • 在 REPEATABLE READ 或 SERIALIZABLE 事务中,在第一条 SQL 语句开始时获取快照并在整个事务中使用。

    • 提交日志(clog)是什么:

      • 表示事务状态的位数组。

      • 使用两个位来指示事务的结果:进行中、已提交、已中止、部分提交。

      • 存储在 $LTDATA/lt_xact/ 目录下的文件集合中。

      • 缓存在内存缓冲区中,共 128 个 8 KB 页面。

    • 当快照显示目标事务已完成时,会查询 clog。

    • 根据快照和 clog,已提交事务的更改可见,而被中止或正在运行的事务的更改不可见。

    • 实际的元组可见性要复杂得多...

提示位

  • 提示位是元组头部的 infomask 字段中的位,用于帮助确定元组的可见性。

  • 它们是为了性能优化而设计的,并不是数据正确性的必需条件。

  • 它们表示由 xmin 或 xmax 所指示的事务是否已提交或中止。共有四个标志位:

    1. HEAP_XMIN_COMMITTED: xmin 事务已提交

    2. HEAP_XMIN_INVALID: xmin 事务已中止

    3. HEAP_XMAX_COMMITTED: xmax 事务已提交

    4. HEAP_XMAX_INVALID: xmax 事务已中止

  • 提示位是如何使用的:

    1. 一个事务检查提示位以查看 xmin 和/或 xmax 事务是否已提交或中止。

    2. 如果提示位已设置,则完成。

    3. 否则,检查提交日志($LTDATA/lt_xact/),并且可能还需要检查子事务层次结构($LTDATA/lt_subtrans/)来确定事务的结果。这是一个耗时的操作。

    4. 设置提示位。它们将在稍后被持久化到磁盘。

  • 设置提示位会写入一个数据页,并且如果启用了页面校验和的话,也会写入 WAL。

3.3.4. 

即使请求的模式与已持有的锁兼容,锁请求也可能等待。

  • 问:你认为事务3是否会继续执行查询?

    1. 事务1:一个长时间运行的 SELECT 正在针对 mytable 进行。

    2. 事务2:运行 “ALTER TABLE mytable ADD COLUMN new_col int;”。由于 ALTER TABLE 请求的 Access Exclusive 锁与事务1持有的 Access Share 锁冲突,所以被阻塞。

    3. 事务3:运行一个针对 mytable 的短 SELECT 查询。

  • 答:事务3会等待直到事务2完成,因为事务2先到并且正在等待。

  • 后来的请求者尊重等待队列中更早的等待者,并不会超越他们。否则,更早的请求者可能会等待过长的时间。

  • 因此,即使是预期可以快速运行的DDL也要:

    • 在非高峰时段执行,和/或

    • 设置锁超时。例如,在DDL之前运行 “SET lock_timeout = '5s';”。如果DDL超时,则重试。

  • 这对轻量级锁并不适用。在极端情况下,一个 LWLock 上的 Exclusive 模式请求可能会由于后来连续不断的 Share 模式请求而等待数十秒。

准备好的事务持有锁

  • 一个准备好的事务将继续持有锁,但由于没有关联的会话,因此不会出现在 pg_stat_activity 中。

  • pg_locks 显示准备好的事务为具有 NULL pid 的条目。检查 pg_prepared_xacts。


3.3.5. 数据完整性验证

数据校验和

  • 目的和用途

    • 每个关系的数据页,包括所有的分支,都有一个在其页头的 16 位校验和。

    • 设计目的是为了检测 I/O 系统(例如,卷管理器、文件系统、磁盘驱动程序、存储固件、存储设备等)引起的损坏。早期检测可以防止损坏的传播。

    • 不是为了检测内存错误。

    • 在整个集群级别启用,可以通过 lt_initdb 的 -k/--data-checksums 参数或者在数据库服务器关闭时通过 lt_checksums 工具启用。默认情况下是禁用的,因为它会影响性能。

    • 运行 "SHOW data_checksums" 可以查看数据校验和是否启用。返回值为 on 或 off。

  • 如何工作

    • 当数据页即将写入磁盘时,计算校验和并设置。

    • 在从磁盘读取页之后,通过比较页头中的值和新计算出的值来验证校验和。

    • 如果验证失败,会发出 WARNING 和 ERROR 消息,导致查询失败。

    • 如果在执行校验和验证之前页头未能通过基本的合理性检查,查询将以相同的 ERROR 消息失败,但不会有指示校验和失败的 WARNING。

WAL CRC

  • WAL 在每个 WAL 记录头中使用一个 32 位的 CRC。

  • 当 WAL 记录放入 WAL 缓冲区时设置 CRC,并在读取 WAL 记录时验证。

用于检测、绕过或修复数据损坏的工具(有些可能是危险的!)

  • 额外模块

    • amcheck: 检测堆表(表、序列)和 B 树索引的逻辑损坏。

    • pg_visibility_mappg_check_frozen()pg_check_visible() 检测可见性映射损坏。

  • 配置参数

    • ignore_checksum_failure

    • zero_damaged_pages

    • ignore_system_indexes


3.3.6. 备份与恢复

备份与恢复方法

  1. 文件系统级别的备份(二进制格式)

  2. 使用 lt_dump/lt_dumpall 的 SQL 导出(文本格式)

  3. 持续归档(二进制格式)

备份与恢复方法的特点

  • SQL 导出和持续归档可以在数据库在线的情况下执行。文件系统级别的备份需要数据库服务器关闭。

  • SQL 导出可以选择性地备份和恢复单个表。其他方法不能只备份或恢复某些特定的表或表空间。

  • SQL 导出通常较小,因为 SQL 脚本只需包含索引创建命令,而不是索引数据。

  • SQL 导出可以加载到更新的主要版本的数据库中。

  • SQL 导出可以将数据库转移到不同的机器架构,例如从 32 位服务器迁移到 64 位服务器。

  • 持续归档可以执行 PITR(基于时间点的恢复)。数据库集群可以恢复到最新状态或特定的时间点。

  • lt_dump 创建的导出是一致的;每个数据库的导出是 lt_dump 开始时数据库的一个快照。lt_dumpall 依次调用 lt_dump 对每个数据库进行操作,因此无法保证整个数据库集群的一致性。

  • lt_dump 在一次事务中转储数据库中的所有数据,发出许多 SELECT 命令。这个长时间运行的事务可能会:

    • 阻塞其他需要强锁模式的操作,如 ALTER TABLE、TRUNCATE、CLUSTER、REINDEX。

    • 导致表和索引膨胀,因为 VACUUM 无法移除死元组。

  • 持续归档的 pg_start_backup()pg_basebackup 在开始时执行一个检查点。用户可以选择检查点速度为“快”或“分散”。

  • 归档恢复和崩溃恢复会清空未记录关系的内容。SQL 导出输出未记录表的内容。

  • lt_dumpall 的 --no-role-passwords 选项使用 pg_roles 而不是 pg_authid 来导出数据库角色。这允许在受限环境中(如 DBaaS)使用 lt_dumpall,因为在这些环境中用户不允许读取 pg_authid 以保护密码。恢复后的角色将具有 NULL 密码。


3.3.7. 流复制

架构

  • 拓扑结构

    • 只能复制整个数据库集群。部分复制是不可能的。

    • 一台主服务器可以向一台或多台备用服务器复制。

    • 每个备用服务器只能从一个主服务器复制。

    • 备用服务器可以将更改级联到其他备用服务器。

    • 主服务器不知道备用服务器的位置。备用服务器通过 primary_conninfo 参数指定的主服务器连接。

      • 例如:primary_conninfo = 'host=192.168.1.50 port=5432 user=foo password=foopass'

  • 主服务器和备用服务器的版本

    • 不同的主要版本不能一起工作。

    • 不同的小版本可以一起工作,因为磁盘格式相同,但是没有正式的支持。建议保持主服务器和备用服务器相同的小版本。

    • 在小版本升级期间,最安全的做法是先更新备用服务器。

  • 进程和数据流

    • 在服务器启动时,备用服务器首先从归档中读取并应用 WAL,接着从 $LTDATA/lt_wal/ 中读取,然后启动一个 walreceiver,它连接到主服务器并从中流式传输 WAL。如果复制连接中断,它会在 5 秒间隔重复此周期,该间隔可以通过 wal_retrieve_retry_interval 配置。

    • 主服务器在接受来自 walreceiver 的连接请求时启动 walsender。

    • walsender 读取并向 walreceiver 发送 WAL。

    • walreceiver 将流式传输的 WAL 写入并刷新到 $LTDATA/lt_wal/ 中,并通知启动进程。

    • 单一的启动进程读取并应用 WAL。

    • walreceiver 定期通知 walsender 复制进度——它已经写入、刷新和应用了多少 WAL。

    • 级联备用服务器也运行 walsenders 和 walreceiver。

  • 复制用户

    • 复制用户需要 REPLICATION 角色属性。

    • REPLICATION 允许用户读取所有用于复制的数据,但不能用于 SELECT 查询消费。

一般管理

  • 备用服务器是只读的。任何对象,包括角色,都不能仅在备用服务器上创建。

  • max_wal_senders 应略高于备用服务器的数量,以便备用服务器在临时意外断开连接后仍能接受连接,而断开连接的 walsender 仍然存在。

  • 可以在备用服务器上进行备份。

  • 不需要设置 archive_timeout 来减少数据丢失窗口。

  • 级联复制减少了主服务器上的负载。

  • 主服务器上的 WAL

    • 如果没有任何措施,主服务器不会关心备用服务器,并删除/回收备用服务器仍需要的旧 WAL 文件。

    • 如果备用服务器请求已经被删除的 WAL,主服务器会发出类似 "ERROR: requested WAL segment 000000020000000300000041 has already been removed" 的消息。

    • 为了让主服务器保留 WAL 文件,可以使用复制槽(首选方法)或设置 keep_wal_size(旧方法)。

    • max_slot_wal_keep_size 限制了由复制槽保留的 WAL 量。

  • 同步复制

    • 如果没有可用的同步备用服务器,事务将在提交期间挂起。

    • 要恢复挂起的事务,移除 synchronous_standby_names 设置并重新加载配置。这会使复制变为异步。

复制延迟的原因

  • 硬件配置:服务器、存储和网络

  • 主服务器上的重负载:主服务器生成的WAL数量如此之大,以至于单一的启动进程无法跟上。

    • 设置 wal_compression = on 以减少WAL的数量。

  • 从慢速归档中检索WAL:备用服务器无法从主服务器获取WAL,因此必须从WAL归档中获取。

  • 回放冲突:某些操作的回放可能被备用服务器上运行的查询所阻止。

    • 这与使用热备用有关。

    • 减少 max_standby_archive_delay 和 max_standby_streaming_delay 以取消冲突查询并尽早恢复WAL回放。

热备用

  • 在服务器处于归档恢复或备用模式时运行只读查询的能力。

  • 回放冲突

    • WAl回放与备用服务器上的查询之间的冲突。

    • 要么延迟WAL回放,要么取消查询。

    • 导致回放冲突的主服务器上的操作包括:

      • 需要Access Exclusive锁的操作:DDL、LOCK、VACUUM(包括自动VACUUM)导致的文件截断。

        • Access Exclusive锁请求被WAL记录,并由备用服务器回放。

      • 删除备用服务器上查询放置临时文件的表空间。

      • 删除备用服务器上有客户端连接的数据库。

      • VACUUM清理备用事务根据其快照仍可见的死亡元组。

      • VACUUM清理备用事务有缓冲区锁定的页面(例如,游标位于页面上)。

    • 回放冲突发生时的情况

      • WAL应用等待最多由max_standby_archive_delay和max_standby_streaming_delay指定的时间(除了DROP DATABASE和ALTER DATABASE SET TABLESPACE的回放)。

      • 然后,在回放DROP DATABASE的情况下终止冲突会话,或者在其他情况下取消冲突查询。

      • 如果一个空闲会话持有锁,则该会话也会被终止。

    • 监控回放冲突

      • 在备用服务器上的 pg_stat_database_conflicts 显示由于每种类型的回放冲突而取消的查询数量。

      • “log_recovery_conflict_waits = on” 记录消息,表明WAL应用已等待超过deadlock_timeout的时间并且等待结束。

        • LOG: recovery still waiting after 1.023 ms: recovery conflict on snapshot

        • DETAIL: Conflicting processes: 1234, 1235

        • LOG: recovery finished waiting after 3.234 ms: recovery conflict on snapshot

    • 最小化因回放冲突而取消的查询数量

      • 避免需要Access Exclusive锁的操作。例如,ALTER TABLE、VACUUM FULL、CLUSTER、REINDEX、TRUNCATE。

      • 通过设置主服务器上的vacuum_truncate存储参数来禁用VACUUM的文件截断。

        • 例如:ALTER TABLE some_table SET (vacuum_truncate = off);

      • 在备用服务器上设置hot_standby_feedback = on。

        • 发送最老的XID给主服务器,反映在pg_stat_replication.backend_xmin中,这是VACUUM决定移除一个死亡元组时考虑的因素。

        • 可能会导致表膨胀,因为死亡元组的移除被延迟。

        • 不能防止所有冲突。

      • 调整备用服务器上的max_standby_streaming_delay/max_standby_archive_delay。

      • 调整主服务器上的vacuum_defer_cleanup_age。

    • 最理想的是拥有独立的备用服务器,一些用于高可用性,另一些则用于容忍陈旧数据的读取工作负载。

监控复制延迟

  • pg_stat_replication

    • 不仅在主服务器上可用,也在级联备用服务器上可用。

    • pg_current_wal_lsn 和视图的 sent_lsn 字段之间存在较大差异可能表明主服务器处于高负载下。

    • 备用服务器上的 sent_lsn 和 pg_last_wal_receive_lsn 之间的差异可能表明网络延迟,或者备用服务器处于高负载下。

  • pg_stat_wal_receiver

    • pg_last_wal_replay_lsn() 和视图的 flushed_lsn 之间存在较大差异表明WAL接收速度比能够回放的速度快。

    • 例如, SELECT pg_wal_lsn_diff(pg_last_wal_replay_lsn(), flushed_lsn) FROM pg_stat_wal_receiver;

  • 存储写入延迟、IOPs(每秒输入输出操作数)和吞吐量以检查繁重的写活动。

  • pg_stat_database_conflicts


3.3.8. 逻辑复制

架构

  • 使用发布和订阅模型

    • 发布是一组其更改将被复制的表。

    • 订阅表示与发布者及其发布的连接。

    • 一个发布者可以发布一个或多个发布。

    • 一个订阅者可以有一个或多个订阅。

    • 一个发布可以有多个订阅者。

    • 一个订阅可以订阅多个发布。

    • 发布可以选择限制它们产生的更改到任何组合的INSERT、UPDATE、DELETE和TRUNCATE。

    • 发布可以限制要复制的行和列。

  • 进程和数据流

    • 参与的进程:发布者上的walsender,订阅者上的订阅工作者(应用工作者,表同步工作者)。

    • 即使使用了一些与walreceiver相关的参数,walreceiver也不会出现。

    1. 在订阅者启动时,除非max_logical_replication_workers为0,否则会启动逻辑复制启动器。

    2. 逻辑复制启动器为每个启用的订阅启动一个应用工作者。

    3. 应用工作者连接到发布者。

    4. 应用工作者为尚未完成初始同步的表启动表同步工作者。这些表同步工作者各自连接到发布者。

    5. 发布者为来自订阅工作者的每个连接请求启动一个walsender。

    6. 表同步工作者的walsender向表同步工作者发送表的初始副本。(初始数据同步/复制)

    7. walsender读取WAL,将更改解码成逻辑复制协议格式,并存储在逻辑解码工作内存中,可能还会存入文件。当事务提交时,walsender将其解码的更改发送给订阅工作者。

    8. 订阅工作者应用接收到的更改。

常规管理

  • 主要限制

    • 发布只能包含表。

    • 数据定义语言(DDL)不会被复制。

      • 首先在订阅者上添加表列,然后在发布者上添加。删除表列时则相反。

    • 序列数据不会被复制。

  • 复制身份

    • 已发布的表必须具有副本身份才能复制UPDATE和DELETE操作。

    • 作为键来识别订阅者上要更新或删除的行。

    • 如果已发布的表没有副本身份,则UPDATE和DELETE在发布者上失败。INSERT成功。

    • 可以是主键(默认)、唯一索引或完整行。

    • 可以通过 ALTER TABLE REPLICA IDENTITY 来配置。

    • 副本身份列的旧值会被WAL记录。

  • 调整性能

    • max_sync_workers_per_subscription

      • 根据max_sync_workers_per_subscription配置,多个表同步工作者(每个表一个)将并行运行。

      • 当订阅中有许多表时,这可能有效地加快初始表同步速度。

复制冲突

  • 由于约束违反或缺少权限,传入更改在订阅者上的应用可能会失败。这就是冲突。

  • 解决冲突:

    1. 如果还没有禁用订阅,可以通过运行 "ALTER SUBSCRIPTION name DISABLE;" 来禁用它。订阅可以配置为在应用工作者检测到任何错误时自动禁用。运行 "ALTER SUBSCRIPTION ... WITH (disable_on_error = on);"

    2. 查看服务器日志中的冲突事务的复制源名称和结束LSN。

    3. 执行以下任一操作:

      • 通过运行 "ALTER SUBSCRIPTION ... SKIP (冲突事务的结束LSN)""SELECT pg_replication_origin_advance(复 制源名称, 冲突事务结束后的下一个LSN)" 来跳过发布者的事务。

      • 在订阅者上修复表数据。

    4. 通过运行 "ALTER SUBSCRIPTION name ENABLE;" 启用订阅。