找回密码
 立即注册
首页 业界区 安全 PostgreSQL 逻辑复制中的表DDL变更执行步骤 ...

PostgreSQL 逻辑复制中的表DDL变更执行步骤

擘塞 2025-6-27 09:46:53
 
PostgreSQL的逻辑复制不会自动同步DDL(第三方插件另说),因此在逻辑复制环境中,当表发布的表执行DDL的时候,如果不停止业务的话,publication(发布节点,主节点)和subscription(订阅节点,从节点)如何执行相关的DDL,才能确保复制正常运行,不受DDL的影响?
结论是:首先在subscription从节点执行DDL,然后再publication主节点执行相关DDL,这样的话,复制不会受到任何影响。
在不停止业务的情况下,如果先在主节点DDL,那么就可能会往表的新增字段中写入数据,当WAL Sender进程解码出来新增的字段,通过apply worker进程在subscription上执行的时候,会因为找不到相关的字段报错。相反,如果先在subscription上执行相关DDL,修改表结构,此时apply worker中的数据并不包含新增字段的数据,是不影响复制的,然后在publication上执行相关DDL,这样就算是新增字段的数据立马写入数据,通过WAL Sender传递到subsacrption上可以完全正确地执行。

以下以t3表为例,t3表是一个发布订阅的表,现在要增加字段,也即执行DDL,可以先从subscription上执行,然后再publication执行,这样整个复制过程不会收到任何影响。
1,主节点逻辑复制槽以及publication信息

1.png

 
2,从节点subscription信息

2.png

 
3,主节点上写入数据

3.png

 
4,从节点正常复制

4.png

 
5,subscription从节点先执行DDL,与此同时,主节点执行DDL之前,t3上再次写入一条测试数据,同时可以正常复制主节点数据

5.png

 
6,publication主节点执行DDL,执行完往新字段写入数据

6.png

 
7,subscription从节点复制正常

7.png

 
主节点测试脚本
  1. db01=# \x
  2. Expanded display is on.
  3. db01=#
  4. db01=# select * from pg_replication_slots;
  5. -[ RECORD 1 ]-------+------------------------------
  6. slot_name           | pgstandby_slave01
  7. plugin              |
  8. slot_type           | physical
  9. datoid              |
  10. database            |
  11. temporary           | f
  12. active              | f
  13. active_pid          |
  14. xmin                |
  15. catalog_xmin        |
  16. restart_lsn         | 1/20518D30
  17. confirmed_flush_lsn |
  18. wal_status          | reserved
  19. safe_wal_size       |
  20. two_phase           | f
  21. conflicting         |
  22. -[ RECORD 2 ]-------+------------------------------
  23. slot_name           | db01_logic_replication_slot01
  24. plugin              | pgoutput
  25. slot_type           | logical
  26. datoid              | 16400
  27. database            | db01
  28. temporary           | f
  29. active              | t
  30. active_pid          | 33412
  31. xmin                |
  32. catalog_xmin        | 1171
  33. restart_lsn         | 1/214B8040
  34. confirmed_flush_lsn | 1/214B8078
  35. wal_status          | reserved
  36. safe_wal_size       |
  37. two_phase           | f
  38. conflicting         | f
  39. db01=#
  40. db01=# \x off
  41. Expanded display is off.
  42. db01=#
  43. db01=#  select * from pg_catalog.pg_publication;
  44.   oid  |        pubname         | pubowner | puballtables | pubinsert | pubupdate | pubdelete | pubtruncate | pubviaroot
  45. -------+------------------------+----------+--------------+-----------+-----------+-----------+-------------+------------
  46. 41149 | master_db01_pulication |       10 | f            | t         | t         | f         | f           | f
  47. (1 row)
  48. db01=#
  49. db01=# SELECT oid, prpubid, prrelid::regclass FROM pg_publication_rel;
  50.   oid  | prpubid | prrelid
  51. -------+---------+---------
  52. 41150 |   41149 | t1
  53. 41151 |   41149 | t2
  54. 41158 |   41149 | t3
  55. (3 rows)
  56. db01=#
  57. db01=# \d t3
  58.                                      Table "public.t3"
  59. Column |            Type             | Collation | Nullable |           Default
  60. --------+-----------------------------+-----------+----------+------------------------------
  61. c1     | integer                     |           | not null | generated always as identity
  62. c2     | character varying(100)      |           |          |
  63. c3     | timestamp without time zone |           |          |
  64. Indexes:
  65.     "t3_pkey" PRIMARY KEY, btree (c1)
  66. Publications:
  67.     "master_db01_pulication"
  68. db01=#
  69. db01=# insert into t3(c1,c2,c3) values (1,'aaa',now());
  70. ERROR:  cannot insert a non-DEFAULT value into column "c1"
  71. DETAIL:  Column "c1" is an identity column defined as GENERATED ALWAYS.
  72. HINT:  Use OVERRIDING SYSTEM VALUE to override.
  73. db01=#
  74. db01=#
  75. db01=#
  76. db01=#
  77. db01=# \d t3
  78.                                      Table "public.t3"
  79. Column |            Type             | Collation | Nullable |           Default
  80. --------+-----------------------------+-----------+----------+------------------------------
  81. c1     | integer                     |           | not null | generated always as identity
  82. c2     | character varying(100)      |           |          |
  83. c3     | timestamp without time zone |           |          |
  84. Indexes:
  85.     "t3_pkey" PRIMARY KEY, btree (c1)
  86. Publications:
  87.     "master_db01_pulication"
  88. db01=#
  89. db01=#
  90. db01=# insert into t3(c2,c3) values ('aaa',now());
  91. INSERT 0 1
  92. db01=#
  93. db01=# select * from t3
  94. db01-# ;
  95. c1 | c2  |            c3
  96. ----+-----+--------------------------
  97.   1 | aaa | 2025-06-26 14:31:50.1698
  98. (1 row)
  99. db01=#
  100. db01=#
  101. db01=# insert into t3(c2,c3) values ('bbb',now());
  102. INSERT 0 1
  103. db01=#
  104. db01=# select * from t3;
  105. c1 | c2  |             c3
  106. ----+-----+----------------------------
  107.   1 | aaa | 2025-06-26 14:31:50.1698
  108.   2 | bbb | 2025-06-26 14:35:15.171404
  109. (2 rows)
  110. db01=# alter table t3 add c4 timestamp;
  111. ALTER TABLE
  112. db01=#
  113. db01=# insert into t3(c2,c3) values ('ccc',now(),now());
  114. ERROR:  INSERT has more expressions than target columns
  115. LINE 1: insert into t3(c2,c3) values ('ccc',now(),now());
  116.                                                   ^
  117. db01=# insert into t3(c2,c3,c4) values ('ccc',now(),now());
  118. INSERT 0 1
  119. db01=#
  120. db01=# select * from t3;
  121. c1 | c2  |             c3             |             c4
  122. ----+-----+----------------------------+----------------------------
  123.   1 | aaa | 2025-06-26 14:31:50.1698   |
  124.   2 | bbb | 2025-06-26 14:35:15.171404 |
  125.   3 | ccc | 2025-06-26 14:37:43.571553 | 2025-06-26 14:37:43.571553
  126. (3 rows)
  127. db01=#
  128. db01=#
复制代码
 
从节点测试脚本
  1. mydb01=#  select * from pg_subscription;
  2. -[ RECORD 1 ]-------+---------------------------------------------------------------------------------
  3. oid                 | 16407
  4. subdbid             | 16388
  5. subskiplsn          | 0/0
  6. subname             | slave_db01_subscription
  7. subowner            | 10
  8. subenabled          | t
  9. subbinary           | f
  10. substream           | f
  11. subtwophasestate    | d
  12. subdisableonerr     | f
  13. subpasswordrequired | t
  14. subrunasowner       | f
  15. subconninfo         | host=8.*.*.129 port=9000 dbname=db01 user=replica_user  password=******
  16. subslotname         | db01_logic_replication_slot01
  17. subsynccommit       | off
  18. subpublications     | {master_db01_pulication}
  19. suborigin           | any
  20. mydb01=# SELECT *, srrelid::regclass FROM pg_subscription_rel;
  21. -[ RECORD 1 ]----------
  22. srsubid    | 16407
  23. srrelid    | 16402
  24. srsubstate | r
  25. srsublsn   | 1/21166E58
  26. srrelid    | t3
  27. -[ RECORD 2 ]----------
  28. srsubid    | 16407
  29. srrelid    | 16390
  30. srsubstate | r
  31. srsublsn   | 1/2116F7E0
  32. srrelid    | t1
  33. -[ RECORD 3 ]----------
  34. srsubid    | 16407
  35. srrelid    | 16396
  36. srsubstate | r
  37. srsublsn   | 1/21171BF0
  38. srrelid    | t2
  39. mydb01=#
  40. mydb01=# \d t3
  41.                                      Table "public.t3"
  42. Column |            Type             | Collation | Nullable |           Default
  43. --------+-----------------------------+-----------+----------+------------------------------
  44. c1     | integer                     |           | not null | generated always as identity
  45. c2     | character varying(100)      |           |          |
  46. c3     | timestamp without time zone |           |          |
  47. Indexes:
  48.     "t3_pkey" PRIMARY KEY, btree (c1)
  49. mydb01=#
  50. mydb01=#
  51. mydb01=# select * from t3;
  52. -[ RECORD 1 ]----------------
  53. c1 | 1
  54. c2 | aaa
  55. c3 | 2025-06-26 14:31:50.1698
  56. mydb01=#
  57. mydb01=# \x off
  58. Expanded display is off.
  59. mydb01=#
  60. mydb01=#
  61. mydb01=# select * from t3;
  62. c1 | c2  |            c3
  63. ----+-----+--------------------------
  64.   1 | aaa | 2025-06-26 14:31:50.1698
  65. (1 row)
  66. mydb01=#
  67. mydb01=#
  68. mydb01=#
  69. mydb01=# \d t3
  70.                                      Table "public.t3"
  71. Column |            Type             | Collation | Nullable |           Default
  72. --------+-----------------------------+-----------+----------+------------------------------
  73. c1     | integer                     |           | not null | generated always as identity
  74. c2     | character varying(100)      |           |          |
  75. c3     | timestamp without time zone |           |          |
  76. Indexes:
  77.     "t3_pkey" PRIMARY KEY, btree (c1)
  78. mydb01=#
  79. mydb01=#
  80. mydb01=# select * from t3;
  81. c1 | c2  |            c3
  82. ----+-----+--------------------------
  83.   1 | aaa | 2025-06-26 14:31:50.1698
  84. (1 row)
  85. mydb01=#
  86. mydb01=#
  87. mydb01=# alter table t3 add c4 timestamp;
  88. ALTER TABLE
  89. mydb01=#
  90. mydb01=# select * from t3;
  91. c1 | c2  |             c3             | c4
  92. ----+-----+----------------------------+----
  93.   1 | aaa | 2025-06-26 14:31:50.1698   |
  94.   2 | bbb | 2025-06-26 14:35:15.171404 |
  95. (2 rows)
  96. mydb01=# select * from t3;
  97. c1 | c2  |             c3             |             c4
  98. ----+-----+----------------------------+----------------------------
  99.   1 | aaa | 2025-06-26 14:31:50.1698   |
  100.   2 | bbb | 2025-06-26 14:35:15.171404 |
  101.   3 | ccc | 2025-06-26 14:37:43.571553 | 2025-06-26 14:37:43.571553
  102. (3 rows)
  103. mydb01=#
复制代码
 

来源:豆瓜网用户自行投稿发布,如果侵权,请联系站长删除

相关推荐

您需要登录后才可以回帖 登录 | 立即注册