如果 PostgreSQL 没有变化,事务是否记录到 WAL?

问题描述

如果行没有更改,我正在尝试找出更改是否反映在 WAL(预写日志)文件中。为了测试它,我在 Postgresql 中创建了一个复制槽来捕获更改。 以下是我已采取的步骤。

ALTER SYstem SET wal_level TO logical;
$ pg_ctl restart
SELECT pg_create_logical_replication_slot('slotname','test_decoding');
CREATE TABLE foo(col1 INTEGER,col2 INTEGER);
ALTER TABLE foo REPLICA IDENTITY FULL;
INSERT INTO foo VALUES(1,2);

然后我在 psql 中执行 SELECT * FROM pg_logical_slot_get_changes('slotname',NULL,NULL);(省略之前的更改)

输出为:

    lsn    | xid |                           data                            
-----------+-----+-----------------------------------------------------------
 0/165B208 | 488 | BEGIN 488
 0/165B208 | 488 | table public.foo: INSERT: col1[integer]:1 col2[integer]:2
 0/165B278 | 488 | COMMIT 488
(3 rows)

然后我执行UPDATE foo SET col2=2 WHERE col1=1;。那么select * from pg_logical_slot_get_changes('slotname',null,null);输出是:

    lsn    | xid |                                                     data                                                      
-----------+-----+---------------------------------------------------------------------------------------------------------------
 0/165B2B0 | 489 | BEGIN 489
 0/165B2B0 | 489 | table public.foo: UPDATE: old-key: col1[integer]:1 col2[integer]:2 new-tuple: col1[integer]:1 col2[integer]:2
 0/165B338 | 489 | COMMIT 489
(3 rows)

看起来 UPDATE 语句更新了 WAL 文件,即使它对表没有影响。但我感到困惑的是,如果我们查看 Postgresql docs 版本 12,这是我正在使用的版本,它在“REPLICA IDENTITY”部分说,

副本身份 此表单更改写入预写日志的信息以识别 更新或删除的行。此选项无效,除非符合逻辑 正在使用复制。 DEFAULT(非系统表的认值)记录旧的 主键列的值(如果有)。 USING INDEX 记录旧值 命名索引覆盖的列,必须是唯一的,不是部分的,不是 可延迟,并且仅包括标记为 NOT NULL 的列。 FULL 记录旧值 行中的所有列。 nothing 不记录有关旧行的信息。 (这是 系统表的认值。)在所有情况下,除非至少有一个旧值,否则不会记录旧值 将记录的列在旧版本和新版本之间有所不同 行。

最后一句指出行的旧版本和新版本必须不同才能被记录。但我看到了相反的情况。我在这里错过了什么?

解决方法

Replica identity 只是逻辑复制消息/协议的一部分,参见 Message format

更新 ... Byte1('K')

将以下 TupleData 子消息标识为键。此字段是可选的,仅当更新更改了作为 REPLICA IDENTITY 索引一部分的任何列中的数据时才存在。 字节1('O')

将以下 TupleData 子消息标识为旧元组。此字段是可选的,仅当发生更新的表将 REPLICA IDENTITY 设置为 FULL 时才存在。

您引用的文档部分参考了上述内容。您显示的槽信息是从整体上查看复制过程。

Replica identity 的用途在此处说明Logical replication

一个已发布的表必须配置一个“副本身份”,以便能够复制 UPDATE 和 DELETE 操作,以便在订阅者端可以识别要更新或删除的适当行。默认情况下,这是主键(如果有)。另一个唯一索引(有某些附加要求)也可以设置为副本标识。如果该表没有任何合适的键,则可以将其设置为副本标识“完整”,这意味着整行成为键。然而,这非常低效,只有在没有其他解决方案时才应用作后备。如果在发布方设置了非“完整”的副本标识,则在订阅方也必须设置包含相同或更少列的副本标识。有关如何设置副本标识的详细信息,请参阅 REPLICA IDENTITY。如果将没有副本标识的表添加到复制 UPDATE 或 DELETE 操作的发布中,则后续的 UPDATE 或 DELETE 操作将导致发布者出错。无论任何副本身份如何,INSERT 操作都可以继续进行。