2025年9月25日: PostgreSQL 18 发布!
支持的版本: 当前 (18) / 17 / 16 / 15 / 14 / 13
开发版本: devel
不支持的版本: 12 / 11 / 10

29.7. 冲突 #

逻辑复制的行为类似于普通 DML 操作,即使数据在订阅节点上已被本地更改,数据仍会被更新。如果传入的数据违反了任何约束,复制将停止。这被称为“冲突”。在复制 UPDATEDELETE 操作时,缺少数据也被视为“冲突”,但不会导致错误,并且此类操作将被简单地跳过。

在以下“冲突”情况下,会触发额外的日志记录,并收集冲突统计信息(显示在 pg_stat_subscription_stats 视图中):

insert_exists #

插入一条违反 NOT DEFERRABLE 唯一约束的行。请注意,要记录冲突键的源和提交时间戳详细信息,应在订阅端启用 track_commit_timestamp。在这种情况下,将引发错误,直到冲突手动解决。

update_origin_differs #

更新一条以前被另一个源修改过的行。请注意,只有在订阅端启用了 track_commit_timestamp 时才能检测到此冲突。目前,无论本地行的来源如何,更新都会被应用。

update_exists #

行的更新值违反了 NOT DEFERRABLE 唯一约束。请注意,要记录冲突键的源和提交时间戳详细信息,应在订阅端启用 track_commit_timestamp。在这种情况下,将引发错误,直到冲突手动解决。请注意,在更新分区表时,如果更新后的行值满足另一个分区约束,导致该行被插入到新分区中,如果新行违反了 NOT DEFERRABLE 唯一约束,则可能会出现 insert_exists 冲突。

update_missing #

要更新的行未找到。在此情况下,更新将被简单地跳过。

delete_origin_differs #

删除一条以前被另一个源修改过的行。请注意,只有在订阅端启用了 track_commit_timestamp 时才能检测到此冲突。目前,无论本地行的来源如何,删除都会被应用。

delete_missing #

要删除的行未找到。在此情况下,删除将被简单地跳过。

multiple_unique_conflicts #

插入或更新一条行违反了多个 NOT DEFERRABLE 唯一约束。请注意,要记录冲突键的源和提交时间戳详细信息,请确保在订阅端启用了 track_commit_timestamp。在这种情况下,将引发错误,直到冲突手动解决。

请注意,还存在其他冲突场景,例如排他约束违反。目前,我们不为它们提供额外的日志详细信息。

逻辑复制冲突的日志格式如下:

LOG:  conflict detected on relation "schemaname.tablename": conflict=conflict_type
DETAIL:  detailed_explanation.
{detail_values [; ... ]}.

where detail_values is one of:

    Key (column_name [, ...])=(column_value [, ...])
    existing local row [(column_name [, ...])=](column_value [, ...])
    remote row [(column_name [, ...])=](column_value [, ...])
    replica identity {(column_name [, ...])=(column_value [, ...]) | full [(column_name [, ...])=](column_value [, ...])}

日志提供以下信息:

LOG
  • schemaname.tablename 标识冲突中涉及的本地关系。

  • conflict_type 是发生的冲突类型(例如,insert_existsupdate_exists)。

DETAIL
  • detailed_explanation 包括修改现有本地行的事务的源、事务 ID 和提交时间戳(如果可用)。

  • Key 部分包含对于 insert_existsupdate_existsmultiple_unique_conflicts 冲突违反唯一约束的本地行的键值。

  • existing local row 部分包含本地行,如果其源与远程行不同(对于 update_origin_differsdelete_origin_differs 冲突),或者如果键值与远程行冲突(对于 insert_existsupdate_existsmultiple_unique_conflicts 冲突)。

  • remote row 部分包含导致冲突的远程插入或更新操作的新行。请注意,对于更新操作,如果值未更改并且已进行 toasted,则新行的列值将为 null。

  • replica identity 部分包含用于搜索要更新或删除的现有本地行的副本身份键值。如果本地关系被标记为 REPLICA IDENTITY FULL,则可能包括整行值。

  • column_name 是列名。对于 existing local rowremote rowreplica identity full 的情况,仅当用户无权访问表的所有列时,才会记录列名。如果存在列名,则它们出现的顺序与相应的列值相同。

  • column_value 是列值。大型列值将被截断为 64 字节。

  • 请注意,在 multiple_unique_conflicts 冲突的情况下,将生成多个 detailed_explanationdetail_values 行,每行都详细说明与不同唯一约束相关的冲突信息。

逻辑复制操作以拥有订阅的角色所拥有的特权执行。目标表上的权限失败将导致复制冲突,目标表上启用的行级安全策略(订阅所有者受其约束)也会导致复制冲突,而无论任何策略是否会拒绝正在复制的 INSERTUPDATEDELETETRUNCATE。此行级安全限制可能会在未来的 PostgreSQL 版本中解除。

产生错误的冲突将停止复制;用户必须手动解决。有关冲突的详细信息可以在订阅端的服务器日志中找到。

解决方案可以通过修改订阅端的数据或权限使其不与传入更改冲突,或者通过跳过与现有数据冲突的事务来完成。当冲突产生错误时,复制将不会继续,逻辑复制工作进程会向订阅端的服务器日志发出以下类型的消息:

ERROR:  conflict detected on relation "public.test": conflict=insert_exists
DETAIL:  Key already exists in unique index "t_pkey", which was modified locally in transaction 740 at 2024-06-26 10:47:04.727375+08.
Key (c)=(1); existing local row (1, 'local'); remote row (1, 'remote').
CONTEXT:  processing remote data for replication origin "pg_16395" during "INSERT" for replication target relation "public.test" in transaction 725 finished at 0/14C0378

包含违反约束的更改的事务的 LSN 和复制源名称可以从服务器日志中找到(在上述情况下,LSN 为 0/14C0378,复制源为 pg_16395)。可以使用 ALTER SUBSCRIPTION ... SKIP 和结束 LSN(即 0/14C0378)来跳过产生冲突的事务。结束 LSN 可以是事务在发布端提交或准备的 LSN。或者,也可以通过调用 pg_replication_origin_advance() 函数来跳过事务。在使用此函数之前,需要暂时禁用订阅,可以通过 ALTER SUBSCRIPTION ... DISABLE 来实现,或者可以使用 disable_on_error 选项来使用订阅。然后,您可以使用 pg_replication_origin_advance() 函数,并提供 node_name(即 pg_16395)和结束 LSN 的下一个 LSN(即 0/14C0379)。可以使用 pg_replication_origin_status 系统视图查看源的当前位置。请注意,跳过整个事务包括跳过可能不违反任何约束的更改。这很容易导致订阅端不一致。关于冲突行的其他详细信息,例如它们的源和提交时间戳,可以在日志的 DETAIL 行中找到。但请注意,只有当订阅端启用了 track_commit_timestamp 时,此信息才可用。用户可以使用此信息来决定保留本地更改还是采用远程更改。例如,上述日志中的 DETAIL 行表明现有行在本地被修改过。用户可以手动执行“远程更改优先”的操作。

streaming 模式为 parallel 时,失败事务的结束 LSN 可能不会被记录。在这种情况下,可能需要将流模式更改为 onoff 并再次引发相同的冲突,以便失败事务的结束 LSN 会写入服务器日志。有关结束 LSN 的用法,请参考 ALTER SUBSCRIPTION ... SKIP

提交更正

如果您在文档中看到任何不正确、与您对特定功能的体验不符或需要进一步说明的内容,请使用 此表单 报告文档问题。