数据库版本:
一、启动两阶段提交
1发布方
- 创建表和发布。
postgres=# CREATE TABLE test (col1 INT, col2 TEXT, PRIMARY KEY(col1));
CREATE TABLE
postgres=# CREATE PUBLICATION pub FOR TABLE test;
2订阅方
- 创建同一个表,并创建一个启用 two_phase 模式的订阅。
- 检查subtwophasestate列以验证它是否启用了two_phase(如果值为e,则开启了)。
postgres=# CREATE TABLE test (col1 INT, col2 TEXT, PRIMARY KEY(col1));
CREATE TABLE
postgres=# CREATE SUBSCRIPTION sub CONNECTION 'dbname=postgres host=172.20.10.6 port=6000 user=ysla password=1qaz!QAZ' PUBLICATION pub WITH (two_phase = on);
NOTICE: created replication slot "sub" on publisher
CREATE SUBSCRIPTION
postgres=# SELECT subtwophasestate FROM pg_subscription;
subtwophasestate
------------------
e
(1 row)
相应日志为
发布节点
订阅节点
3发布方
- 开始交易。
- 插入一些数据。
- 准备事务并检查 GID。
postgres=# BEGIN;
BEGIN
postgres=*# INSERT INTO test VALUES (7,'aa');
INSERT 0 1
postgres=*# PREPARE TRANSACTION 't1';
PREPARE TRANSACTION
操作的时候由于疏忽,没启动prepare transaction ,这点需要注意,要启动要启动prepare transaction,需要到postgresql.conf配置文件中,将参数max_prepare_transactions设置为非零的值,然后重启数据库服务即可 。
参数修改后,正确的如下
4订阅方
- 检查订户端并查看生成的准备好的事务 GID 也在那里复制。
postgres=# SELECT * FROM pg_prepared_xacts;
此时我们检查订阅节点的pg_twophase目录,该目录包含预备事务的状态文件
对于这个二进制文件,我们可以用hexdump查看内容,例如:
5发布方
- 提交准备好的事务。
- 观察准备好的事务 GID 现在已经消失(它已提交)。
- 选择插入的数据。
postgres=# COMMIT PREPARED 't1';
COMMIT PREPARED
postgres=# SELECT * FROM pg_prepared_xacts;
transaction | gid | prepared | owner | database
------------+-----+----------+-------+----------
(0 rows)
postgres=# SELECT * FROM test;
a | b
---+----
7 | aa
(1 row)
6订阅方
- 订阅方生成的 GID 也消失了(已提交)。
- 选择显示已复制发布的数据。
postgres=# SELECT * FROM pg_prepared_xacts;
transaction | gid | prepared | owner | database
------------+-----+----------+-------+----------
(0 rows)
postgres=# SELECT * from test;
a | b
---+----
7 | aa
(1 row)
订阅节点的pg_twophase目录里的对应事务状态的文件已经不存在了
二、不启动两阶段提交
1发布方
- 创建表和发布。
postgres=# CREATE TABLE test2 (col1 INT, col2 TEXT, PRIMARY KEY(col1));
CREATE TABLE
postgres=# CREATE PUBLICATION pub FOR TABLE test2;
2订阅方
- 创建同一个表,并创建一个启用 two_phase 模式的订阅。
- 检查subtwophasestate列以验证它是否启用了two_phase(如果值为e,则开启了)。
postgres=# CREATE TABLE test2 (col1 INT, col2 TEXT, PRIMARY KEY(col1));
CREATE TABLE
postgres=# CREATE SUBSCRIPTION sub2 CONNECTION 'dbname=postgres host=172.20.10.6 port=6000 user=ysla password=1qaz!QAZ' PUBLICATION pub2 WITH (two_phase = off);
NOTICE: created replication slot "sub2" on publisher
CREATE SUBSCRIPTION
postgres=# SELECT subname,subtwophasestate FROM pg_subscription;
subname | subtwophasestate
---------+------------------
sub | e
sub2 | d
(2 rows)
3发布方
- 开始交易。
- 插入一些数据。
- 准备事务并检查 GID。
postgres=# BEGIN;
BEGIN
postgres=*# INSERT INTO test2 VALUES (22,'aa');
INSERT 0 1
postgres=*# PREPARE TRANSACTION 't2';
PREPARE TRANSACTION
postgres=# SELECT * FROM pg_prepared_xacts;
transaction | gid | prepared | owner | database
-------------+-----+-------------------------------+----------+----------
749 | t2 | 2022-04-24 19:12:27.372256+08 | postgres | postgres
(1 row)
4订阅方
- 检查订户端并查看生成的准备好的事务 GID 也在那里复制。
postgres=# SELECT * FROM pg_prepared_xacts;
transaction | gid | prepared | owner | database
-------------+------------------+-------------------------------+----------+----------
1951 | pg_gid_24607_749 | 2022-04-24 19:12:27.602742+08 | postgres | postgres
(1 row)
postgres=# select * from test2;
col1 | col2
------+------
(0 rows)
此时在订阅节点的pg_twophase目录下,也传输过来了对应的状态文件
5发布方
- 提交准备好的事务。
- 观察准备好的事务 GID 现在已经消失(它已提交)。
- 选择插入的数据。
6订阅方
- 订阅方生成的 GID消失(提交失败)。
- 不显示已复制发布的数据。
总结
根据测试结果来看:
1、逻辑复制开启two_phase,主库COMMIT PREPARE之后,订阅节点才会把事务提交,看到结果,但是在PREPARE TRANSACTION阶段,已经把准备好的事务发送到订阅节点了。
2.逻辑复制不开启two_phase,在PREPARE TRANSACTION阶段,会把两阶段提交的状态文件传输到订阅节点,但就算发布节点COMMIT PREPARE了,因为没使用two_phase,发布节点提交成功,而订阅节点提交失败。
因此,如果在逻辑复制不使用two_phase的时候,就只能通过正常的BEGIN;COMMIT;的方式把事务发送给订阅节点 。
欢迎交流。