PostgreSQL 13、14中逻辑复制/解码改进

本文涉及的产品
云原生数据库 PolarDB PostgreSQL 版,标准版 2核4GB 50GB
云原生数据库 PolarDB MySQL 版,通用型 2核4GB 50GB
简介: PostgreSQL 13、14中逻辑复制/解码改进

PostgreSQL 13、14中逻辑复制/解码改进


最近写了一篇关于Patroni如何解决PG集群中逻辑复制槽故障转移问题的博客:

https://www.percona.com/blog/how-patroni-addresses-the-problem-of-the-logical-replication-slot-failover-in-a-postgresql-cluster/

事实上,没有什么比这个问题更能伤害逻辑复制了。即使在写本文时,也可以看到目前没有用户试图解决这个问题。感谢Patroni社区以出色的方式解决了这个问题:无需补丁、无需扩展,完全无创的解决方案。

随着最大的缺陷消失,我们预计会有越来越多的用户开始研究或重新考虑逻辑复制,尤其是那些由于实际困难而放弃它的用户。我想让他们知道PG13和14等版本中,还有更多与逻辑复制/解码相关的令人兴奋的新功能。在进入新特性前,让我们看看旧版本中逻辑复制的其他问题。


内存使用和磁盘使用


PG过去只为内存中每个事务保留4096次更改(max_changes_in_memory)。如果有一个非常长的事务,其余的更改将作为溢出文件溢出到磁盘。这里有2个重要含义。1)如果每次变化很大,若有子事务,内存消耗很容易跑到几个GB。这甚至会影响主机的稳定性和OOM启动的机会。2)如果更改非常小,若有太多的小更改,并事务很长,就会溢出到磁盘,造成IO开销。


大量复制延迟和CPU负载


许多用户几乎经常抱怨他们不断看到巨大的复制延迟。仔细检查显示WAL sender进程正在消耗大量CPU。单核饱和是最常见的情况。很多时候,更进一步分析显示存在长时间运行的事务或大量数据加载并导致溢出文件的生成。系统正忙于检查溢出文件并准备提交顺序,需要将其发送到逻辑副本。

同样,我们见证了一些用户选择逻辑复制以减少主节点负载的案例。但是WAL sender在逻辑解码期间的复杂性抹杀了所有潜在的收益。这些问题对PG社区来说并不陌生。事实上,关于问题及修复的讨论大约在PG10发布同时开始。好消息是,这些在最近发展中得到了解决。


PG13中的改进


内存和磁盘使用问题在PG13已经基本解决。当添加这了这些改动后,max_changes_in_memory(4096)不再使用了。相反,PG13会跟踪所有事务的总内存使用量和单个事务的内存使用量。引入了一个新参数logical_decoding_work_mem。仅当超过限制时,缓冲区才会溢出到磁盘。并且只有消耗最多内存的最大事务才会成为溢出到磁盘的受害者。这更加智能,减少了不必要的磁盘溢出。

参考:

ReorderBufferCheckMemoryLimit (src/backend/replication/logical/reorderbuffer.c)


PG14中的改进


logical_decoding_work_mem已满时溢出到磁盘是一种想法。但是如何将改动直接传输给订阅者而不是溢出到磁盘,这是PG14中的主要改进。由于我们处理的是正在运行的事务,所以这并不是说说那么简单。逻辑复制的整体逻辑和特性必须经历巨大变化。但是PG14引入了将reorderbuffer流式传输到订阅者而不是先溢出到磁盘的选项。显然,流式传输正在运行的事务这个新功能需要复制协议的改进。新日志信息格式例如“Stream Start”、“Stream Stop”、“Stream Commit”、“Stream Abort”等添加到了复制协议中。参考:

https://www.postgresql.org/docs/14/protocol-logicalrep-message-formats.html

输出插件接口也需要相应改进。这也是PG14中的改进,参考提交45fdc9738b了解更多详细信息,并参阅PostgreSQL文档。

当超过logical_decoding_work_mem时,使用流。这并不意味着buffer不会溢出到磁盘。如果流不可用,保留使用溢出到磁盘的选项。如果当前可用的信息不足以解码,就会发生这种情况。

提交7259736a6e5b7c7588fff9578370736a6648acbb总结了重大改进

1)当达到logical_decoding_work_mem内存限制后,并不是将事务序列化到磁盘,而是使用内存部分的改动并调用流API方法(commit:45fdc9738b)。但是,有时如果我们有不完整的toast或者预测插入,会溢出到磁盘,因为无法生成完整的元组和流,一旦获得完整的元组旧会流式传输包括序列化更改在内的事务。

2)由于立即在WAL中进行了分配(将 subxact 与顶级 xact 相关联),并且在每个命令结束时记录了失效信息,我们可以进行这种增量处理。由提交0bead9af48和c55040ccd0添加。

3)现在可以流式正在运行的事务,当输出插件查询catalog(系统和用户自定义)时,并发的abort可能会造成故障。通过这样处理这个故障:系统表扫描方法API返回ERRCODE_TRANSACTION_ROLLBACK给后端服务或者解码特定未提交事务的WAL Sender。接收到这个错误码的解码逻辑终止当前事务的解码,并继续解码其他事务。


如何配置


必要的功能仅在PG14中使用。客户端需要在streaming开启的情况下初始化复制连接。为次,CREATE SUBSCRIPTION采用了一个输入参数“streaming”,默认关闭。下面是一个例子:


CREATE SUBSCRIPTION sub1 CONNECTION 'host=pg0 port=5432 dbname=postgres user=postgres password=xxxx' PUBLICATION tap_pub WITH (streaming = on);

请记下新参数streaming=on。它指定是否为此订阅启用对正在进行的事务流式传输。或者可以修改现有订阅:


ALTER SUBSCRIPTION sub1 SET(STREAMING = ON)

监控改进


监控方面主要由2个改进。

监控初始数据COPY

PG14允许用户使用新的监控试图pg_stat_progress_copy来监控COPY命令的进度。当有人设置逻辑复制时,这是一个很大的增值。下面是一个select * from pg_stat_progress_copy ;输出示例,以及PUBLISHER 端使用\watch的结果:


  Wed 23 Feb 2022 07:01:46 AM UTC (every 1s)
 pid  | datid | datname  | relid | command | type | bytes_processed | bytes_total | tuples_processed | tuples_excluded
------+-------+----------+-------+---------+------+-----------------+-------------+------------------+-----------------
 2034 | 16401 | postgres | 16390 | COPY TO | PIPE |       932960052 |           0 |          9540522 |               0
(1 row)
                                      Wed 23 Feb 2022 07:01:47 AM UTC (every 1s)
 pid  | datid | datname  | relid | command | type | bytes_processed | bytes_total | tuples_processed | tuples_excluded
------+-------+----------+-------+---------+------+-----------------+-------------+------------------+-----------------
 2034 | 16401 | postgres | 16390 | COPY TO | PIPE |       976060287 |           0 |          9979509 |               0
(1 row)

因为我们知道表中由多少元组,所以我们不难理解它的进度。订阅方也可以进行类似的监控:


                                       Wed 23 Feb 2022 07:01:46 AM UTC (every 1s)
 pid  | datid | datname  | relid |  command  |   type   | bytes_processed | bytes_total | tuples_processed | tuples_excluded
------+-------+----------+-------+-----------+----------+-----------------+-------------+------------------+-----------------
 1204 | 14486 | postgres | 16385 | COPY FROM | CALLBACK |       912168274 |           0 |          9328360 |               0
(1 row)
                                         Wed 23 Feb 2022 07:01:47 AM UTC (every 1s)
 pid  | datid | datname  | relid |  command  |   type   | bytes_processed | bytes_total | tuples_processed | tuples_excluded
------+-------+----------+-------+-----------+----------+-----------------+-------------+------------------+-----------------
 1204 | 14486 | postgres | 16385 | COPY FROM | CALLBACK |       948074690 |           0 |          9694752 |               0
(1 row)

监控逻辑复制


可以通过PG14提供的新视图监控逻辑复制:PUBLISHER 端pg_stat_replication_slot。即使我们不适应新的流式传输功能,这也很有用,因为生成溢出文件的可能性更高。

    postgres=# select * from pg_stat_replication_slots ;
     slot_name | spill_txns | spill_count | spill_bytes | stream_txns | stream_count | stream_bytes | total_txns | total_bytes | stats_reset
    -----------+------------+-------------+-------------+-------------+--------------+--------------+------------+-------------+-------------
     sub       |          1 |          34 |  2250000000 |           0 |            0 |            0 |       2701 |     1766040 |
    (1 row)


    正如上面的案例中看到的,有一个庞大的事务导致了大量的溢出文件。可以使用函数pg_stat_reset_replication_slot();重置与特定slot相关的统计信息:


      postgres=# select pg_stat_reset_replication_slot('sub');
       pg_stat_reset_replication_slot
      --------------------------------
       (1 row)
        postgres=# select * from pg_stat_replication_slots ;
         slot_name | spill_txns | spill_count | spill_bytes | stream_txns | stream_count | stream_bytes | total_txns | total_bytes |          stats_reset          
        -----------+------------+-------------+-------------+-------------+--------------+--------------+------------+-------------+-------------------------------
         sub       |          0 |           0 |           0 |           0 |            0 |            0 |          0 |           0 | 2022-02-23 15:39:08.472519+00
        (1 row)


        启用流式传输后,我们可以获得正在进行的事务流式传输的详细信息:


                                                                 Wed 23 Feb 2022 03:58:53 PM UTC (every 2s)
           slot_name | spill_txns | spill_count | spill_bytes | stream_txns | stream_count | stream_bytes | total_txns | total_bytes |         stats_reset         
          -----------+------------+-------------+-------------+-------------+--------------+--------------+------------+-------------+-----------------------------
           sub       |          1 |           9 |   603980550 |           0 |           29 |   1914455250 |        242 |  1914488162 | 2022-02-23 15:55:46.8994+00
          (1 row)


          建议调整logical_decoding_work_mem 值,默认64MB,来设置我们可以为每个walsender进程花费的最大内存量。使用它,可以避免大量溢出到磁盘,同时避免过多内存使用。


          postgres=# ALTER SYSTEM SET logical_decoding_work_mem = '512MB';
          ALTER SYSTEM
          postgres=# select pg_reload_conf();

          结论


          本文,鼓励过去由于逻辑复制缺陷而放弃逻辑复制的用户重新考虑使用它。因为PG13和14和Patroni解决了大部分困难。冗长的批量事务会对逻辑复制造成严重影响,之前版本非常严重,随着改进,很大程度上得到了缓解,预计将大大减少发布端的负载。

          然而,并不意味着这是完美的。社区和开发人员知道更多需要改进的地方,尤其是订阅端的改进。启动即将到来的版本中会有这样的变化。


          原文


          https://www.percona.com/blog/logical-replication-decoding-improvements-in-postgresql-13-and-14/

          相关实践学习
          使用PolarDB和ECS搭建门户网站
          本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
          阿里云数据库产品家族及特性
          阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
          目录
          相关文章
          |
          存储 SQL 关系型数据库
          【面试题精讲】MySQL逻辑架构
          【面试题精讲】MySQL逻辑架构
          |
          存储 缓存 关系型数据库
          高性能Mysql-逻辑架构
          高性能Mysql-逻辑架构
          |
          SQL 缓存 Oracle
          第04章 逻辑架构【1.MySQL架构篇】【MySQL高级】2
          第04章 逻辑架构【1.MySQL架构篇】【MySQL高级】2
          149 0
          |
          28天前
          |
          存储 关系型数据库 数据库
          【赵渝强老师】PostgreSQL的逻辑存储结构
          PostgreSQL的逻辑存储结构包括数据库集群、数据库、表空间、段、区、块等。每个对象都有唯一的对象标识符OID,并存储于相应的系统目录表中。集群由单个服务器实例管理,包含多个数据库、用户及对象。表空间是数据库的逻辑存储单元,用于组织逻辑相关的数据结构。段是分配给表、索引等逻辑结构的空间集合,区是段的基本组成单位,而块则是最小的逻辑存储单位。
          【赵渝强老师】PostgreSQL的逻辑存储结构
          |
          4月前
          |
          安全 关系型数据库 MySQL
          揭秘MySQL海量数据迁移终极秘籍:从逻辑备份到物理复制,解锁大数据迁移的高效与安全之道
          【8月更文挑战第2天】MySQL数据量很大的数据库迁移最优方案
          788 17
          |
          4月前
          |
          SQL 关系型数据库 数据库
          [postgresql]逻辑备份与还原
          [postgresql]逻辑备份与还原
          |
          6月前
          |
          存储 关系型数据库 MySQL
          MySQL数据库——InnoDB引擎-逻辑存储结构(表空间、段、区、页、行)
          MySQL数据库——InnoDB引擎-逻辑存储结构(表空间、段、区、页、行)
          140 7
          |
          6月前
          |
          存储 关系型数据库 MySQL
          【MySQL技术内幕】4.2-InnoDB逻辑存储结构
          【MySQL技术内幕】4.2-InnoDB逻辑存储结构
          55 0
          |
          7月前
          |
          SQL 存储 缓存
          mysql 逻辑架构
          mysql 逻辑架构
          |
          7月前
          |
          SQL 关系型数据库 分布式数据库
          PolarDB for PostgreSQL逻辑复制问题之逻辑复制冲突如何解决
          PolarDB for PostgreSQL是基于PostgreSQL开发的一款云原生关系型数据库服务,它提供了高性能、高可用性和弹性扩展的特性;本合集将围绕PolarDB(pg)的部署、管理和优化提供指导,以及常见问题的排查和解决办法。