PostgresQL:整个事务是否总是写入复制槽?
PostgresQL: Is an entire transaction always written to a replication slot?
我已经创建了一个复制槽:
SELECT * FROM pg_create_logical_replication_slot('boxoffice_slot', 'test_decoding');
事务中的每个步骤在复制槽中都有自己的行。这是一个例子:
lsn | xid | data
-------------+---------+---------------
34A/7000028 | 1311904 | BEGIN 1311904
34A/70020E0 | 1311904 | table cad.purchases: INSERT: id[integer]:754862
34A/70020E1 | 1311904 | table cad.purchases: INSERT: id[integer]:754863
34A/7000028 | 1311904 | COMMIT 1311904
问题:
在事务生命周期的什么时候事务步骤开始写入复制槽?
是否可以在提交事务之前将事务步骤写入复制槽?
换句话说,是否有可能在任何给定时间只有一半事务写入复制槽,如下所示:
lsn | xid | data
-------------+---------+---------------
34A/7000028 | 1311904 | BEGIN 1311904
34A/70020E0 | 1311904 | table cad.purchases: INSERT: id[integer]:754862
非常感谢您对此的见解。
事务中的所有内容都会在事务发生时写入复制槽。如果事务被回滚,则回滚被输入到 WAL 中。如果流在事务中间中断,则事务不会在订阅者端提交,至少在重新建立连接之前不会。
测试这个的一种方法是创建一个复制槽,然后在另一个终端中,使用 pg_recvlogical
:
查看更改
pg_recvlogical -d postgres --slot <slot_name> --start --verbose -f -
如果您 BEGIN
一个事务,创建一个 table 并向其中插入一百万行,您会看到 write up to
和 flush to
值在增加,即使交易没有得到 committed/rolled 回来。这告诉我们更改正在传播到接收端:
# pg_recvlogical -d postgres --slot regression_slot --start --verbose -f -
pg_recvlogical: starting log streaming at 0/0 (slot regression_slot)
pg_recvlogical: streaming initiated
pg_recvlogical: confirming write up to 0/0, flush to 0/0 (slot regression_slot)
pg_recvlogical: confirming write up to 0/DF5E620, flush to 0/DF5E620 (slot regression_slot)
pg_recvlogical: confirming write up to 0/DF5E620, flush to 0/DF5E620 (slot regression_slot)
pg_recvlogical: confirming write up to 0/12E01AB8, flush to 0/12E01AB8 (slot regression_slot)
pg_recvlogical: confirming write up to 0/12E01AF0, flush to 0/12E01AF0 (slot regression_slot)
此外,如果您在交易结束前后在您的终端中查看 pg_current_wal_lsn()
,您将看到 LSN 增加,即使您回滚了:
pg_current_wal_lsn
--------------------
0/DF5E620
(1 row)
postgres=# begin;
BEGIN
postgres=# select pg_current_wal_lsn();
pg_current_wal_lsn
--------------------
0/DF5E620
(1 row)
postgres=# insert into abc values (generate_series(1,1000000),'foobar');
INSERT 0 1000000
postgres=# select pg_current_wal_lsn();
pg_current_wal_lsn
--------------------
0/1243C000
(1 row)
postgres=# rollback;
ROLLBACK
postgres=# select pg_current_wal_lsn();
pg_current_wal_lsn
--------------------
0/12E01AB8
(1 row)
交易根本没有写入复制槽。它们被写入 WAL。肯定会发生只有部分事务写入 WAL 的情况,例如,如果处理被中断。
部分写入的事务将在会话结束时自动标记为回滚(如果服务器崩溃,则在恢复期间)。
复制槽保存的数据不超过 LSN(日志序列号)。它们是在 WAL 中标记位置的持久数据结构。这样主服务器就不会丢弃消费者仍然需要的任何 WAL。当消费者处理 WAL 时,它会推进复制槽的 LSN,以便主可以丢弃已经使用的 WAL。
根据以下测试我得出结论,事务只有在提交后才会插入到复制槽中。
testing=# BEGIN;
BEGIN
testing=# SELECT * FROM pg_logical_slot_get_changes('testing_slot', NULL, NULL);
lsn | xid | data
-----+-----+------
(0 rows)
testing=# insert into person values (generate_series(1,10));
INSERT 0 10
testing=# SELECT * FROM pg_logical_slot_get_changes('testing_slot', NULL, NULL);
lsn | xid | data
-----+-----+------
(0 rows)
testing=# COMMIT;
COMMIT
testing=# SELECT * FROM pg_logical_slot_get_changes('testing_slot', NULL, NULL);
lsn | xid | data
------------+----------+-----------------------------------------------------------
D/23426BC0 | 16171153 | BEGIN 16171153
D/23426BC0 | 16171153 | table public.person: INSERT: name[character varying]:'1'
D/23427078 | 16171153 | table public.person: INSERT: name[character varying]:'2'
D/234270B8 | 16171153 | table public.person: INSERT: name[character varying]:'3'
D/234270F8 | 16171153 | table public.person: INSERT: name[character varying]:'4'
D/23427138 | 16171153 | table public.person: INSERT: name[character varying]:'5'
D/23427178 | 16171153 | table public.person: INSERT: name[character varying]:'6'
D/234271B8 | 16171153 | table public.person: INSERT: name[character varying]:'7'
D/234271F8 | 16171153 | table public.person: INSERT: name[character varying]:'8'
D/23427238 | 16171153 | table public.person: INSERT: name[character varying]:'9'
D/23427278 | 16171153 | table public.person: INSERT: name[character varying]:'10'
D/23427320 | 16171153 | COMMIT 16171153
(12 rows)
我已经创建了一个复制槽:
SELECT * FROM pg_create_logical_replication_slot('boxoffice_slot', 'test_decoding');
事务中的每个步骤在复制槽中都有自己的行。这是一个例子:
lsn | xid | data
-------------+---------+---------------
34A/7000028 | 1311904 | BEGIN 1311904
34A/70020E0 | 1311904 | table cad.purchases: INSERT: id[integer]:754862
34A/70020E1 | 1311904 | table cad.purchases: INSERT: id[integer]:754863
34A/7000028 | 1311904 | COMMIT 1311904
问题:
在事务生命周期的什么时候事务步骤开始写入复制槽?
是否可以在提交事务之前将事务步骤写入复制槽?
换句话说,是否有可能在任何给定时间只有一半事务写入复制槽,如下所示:
lsn | xid | data
-------------+---------+---------------
34A/7000028 | 1311904 | BEGIN 1311904
34A/70020E0 | 1311904 | table cad.purchases: INSERT: id[integer]:754862
非常感谢您对此的见解。
事务中的所有内容都会在事务发生时写入复制槽。如果事务被回滚,则回滚被输入到 WAL 中。如果流在事务中间中断,则事务不会在订阅者端提交,至少在重新建立连接之前不会。
测试这个的一种方法是创建一个复制槽,然后在另一个终端中,使用 pg_recvlogical
:
pg_recvlogical -d postgres --slot <slot_name> --start --verbose -f -
如果您 BEGIN
一个事务,创建一个 table 并向其中插入一百万行,您会看到 write up to
和 flush to
值在增加,即使交易没有得到 committed/rolled 回来。这告诉我们更改正在传播到接收端:
# pg_recvlogical -d postgres --slot regression_slot --start --verbose -f -
pg_recvlogical: starting log streaming at 0/0 (slot regression_slot)
pg_recvlogical: streaming initiated
pg_recvlogical: confirming write up to 0/0, flush to 0/0 (slot regression_slot)
pg_recvlogical: confirming write up to 0/DF5E620, flush to 0/DF5E620 (slot regression_slot)
pg_recvlogical: confirming write up to 0/DF5E620, flush to 0/DF5E620 (slot regression_slot)
pg_recvlogical: confirming write up to 0/12E01AB8, flush to 0/12E01AB8 (slot regression_slot)
pg_recvlogical: confirming write up to 0/12E01AF0, flush to 0/12E01AF0 (slot regression_slot)
此外,如果您在交易结束前后在您的终端中查看 pg_current_wal_lsn()
,您将看到 LSN 增加,即使您回滚了:
pg_current_wal_lsn
--------------------
0/DF5E620
(1 row)
postgres=# begin;
BEGIN
postgres=# select pg_current_wal_lsn();
pg_current_wal_lsn
--------------------
0/DF5E620
(1 row)
postgres=# insert into abc values (generate_series(1,1000000),'foobar');
INSERT 0 1000000
postgres=# select pg_current_wal_lsn();
pg_current_wal_lsn
--------------------
0/1243C000
(1 row)
postgres=# rollback;
ROLLBACK
postgres=# select pg_current_wal_lsn();
pg_current_wal_lsn
--------------------
0/12E01AB8
(1 row)
交易根本没有写入复制槽。它们被写入 WAL。肯定会发生只有部分事务写入 WAL 的情况,例如,如果处理被中断。
部分写入的事务将在会话结束时自动标记为回滚(如果服务器崩溃,则在恢复期间)。
复制槽保存的数据不超过 LSN(日志序列号)。它们是在 WAL 中标记位置的持久数据结构。这样主服务器就不会丢弃消费者仍然需要的任何 WAL。当消费者处理 WAL 时,它会推进复制槽的 LSN,以便主可以丢弃已经使用的 WAL。
根据以下测试我得出结论,事务只有在提交后才会插入到复制槽中。
testing=# BEGIN;
BEGIN
testing=# SELECT * FROM pg_logical_slot_get_changes('testing_slot', NULL, NULL);
lsn | xid | data
-----+-----+------
(0 rows)
testing=# insert into person values (generate_series(1,10));
INSERT 0 10
testing=# SELECT * FROM pg_logical_slot_get_changes('testing_slot', NULL, NULL);
lsn | xid | data
-----+-----+------
(0 rows)
testing=# COMMIT;
COMMIT
testing=# SELECT * FROM pg_logical_slot_get_changes('testing_slot', NULL, NULL);
lsn | xid | data
------------+----------+-----------------------------------------------------------
D/23426BC0 | 16171153 | BEGIN 16171153
D/23426BC0 | 16171153 | table public.person: INSERT: name[character varying]:'1'
D/23427078 | 16171153 | table public.person: INSERT: name[character varying]:'2'
D/234270B8 | 16171153 | table public.person: INSERT: name[character varying]:'3'
D/234270F8 | 16171153 | table public.person: INSERT: name[character varying]:'4'
D/23427138 | 16171153 | table public.person: INSERT: name[character varying]:'5'
D/23427178 | 16171153 | table public.person: INSERT: name[character varying]:'6'
D/234271B8 | 16171153 | table public.person: INSERT: name[character varying]:'7'
D/234271F8 | 16171153 | table public.person: INSERT: name[character varying]:'8'
D/23427238 | 16171153 | table public.person: INSERT: name[character varying]:'9'
D/23427278 | 16171153 | table public.person: INSERT: name[character varying]:'10'
D/23427320 | 16171153 | COMMIT 16171153
(12 rows)