PostgreSQL 14中两阶段提交的逻辑解码

本文涉及的产品
云原生数据库 PolarDB MySQL 版,通用型 2核4GB 50GB
云原生数据库 PolarDB PostgreSQL 版,标准版 2核4GB 50GB
简介: PostgreSQL 14中两阶段提交的逻辑解码

PostgreSQL 14中两阶段提交的逻辑解码


正文


Fujitsu OSS团队和PostgreSQL开源社区合作在PG14中添加了在逻辑复制中对两阶段提交进行解密的功能。下面看看这项功能是什么?


背景


两阶段提交是事务以两阶段进行提交的一种机制。通常在分布式数据库中用于保证一致性。事务的两阶段是PREPARE阶段和COMMIT/ROLLBACK阶段。PG中两阶段提交的命令是:

PREPARE TRANSACTION

COMMIT PREPARED

ROLLBACK PREPARED

PG在8.0版本已经支持了两阶段提交,10.0版本支持逻辑复制。但是逻辑复制中一直都不支持两阶段提交。单实例中已经支持了PREPARE TRANSACTION、COMMIT PREPARED和ROLLBACK PREPARED命令,但是当这些命令需要逻辑复制到备机时,他们不再保持原始含义。PREPARE TRANSACTION命令被视为NOP,而根本没有解码。COMMIT PREPARED命令被视为COMMIT,ROLLBACK PREPARED命令被视为ABORT。


什么是两阶段提交


两阶段提交是一种原子提交协议,有助于维护分布式数据库之间的一致性。提供数据库内原子性的普通提交不足以为跨数据库的事务提供一致性。为说明这个问题,我们举一个例子:

1) John在A银行有300$

2) Mark在B银行有100$

3) John想给Mark转100$

事务进行过程中,需要从A银行提取100$到银行B。事务结束的时候,应该都有200$.如果在转账的过程中,任何时候任何一笔交易失败,那么账户状态应该恢复到转账开始前的状态。事务可能因各种原因而失败。如果在事务提交之前发生任何中断,则该事务会回滚。在我们的示例中,如果John的账户中扣除金额时发生中断,那么中断口John的账户不应该减少。这就是简单的提交如何保持数据库内的一致性。

但是我们考虑这样一种情况,即从John账户中扣除100$的事务在一次提交时成功,但向Mark在B银行的账户中添加100$的事务失败而被回滚。然后此操作结束后,虽然John账户已扣款,但Mark将不会收到该金额。100$消失了。在处理分布式事务时,简单的提交有可能失败。


分布式事务的分步执行


对于两阶段提交,其中一个数据库充当分布式事务的协调器。

阶段1

一个数据库开始应用事务,然后做Prepare。它以prepare消息形式发送prepared事务到其它数据库。第2个数据库获取到Prepare消息,然后prepare该事务。Prepare涉及事务中的修改,但不提交。这些脏数据写到磁盘以持久化。一旦所有数据库都prepare了事务,并且有关该事务的所有信息都存储到磁盘上,prepare阶段就完成了。

阶段2


接下来,仲裁器启动提交阶段。如果第2个数据库由于某种原因未能准备事务,则仲裁器启动回滚阶段。因此根据prepare是否成功,事务要么提交,要么回滚。在最后提交阶段发生中断是可以恢复的,因为所需的prepare事务已经写入磁盘并可以重新应用。

两阶段提交与单实例数据库并不相关,但若数据复制跨多个数据库实例时,就相关了。

逻辑复制中支持两阶段提交非常重要。


功能概述


PG14版本前,逻辑复制事务仅在事务提交后才被解码和复制。这是为了避免复制事务可能最终被中止。

提交时解码事务

PG14的逻辑复制支持PREPARE TRANSACTION、COMMIT PREPARED和ROOLBACK PREPARED命令。当PREPARE TRANSACTION命令解码时,事务被解码并复制。PREPARE TRANSACTION就像WAL SENDER中COMMIT一样启动事务重放和解码。

prepare时解码事务

我们还定义了新的插件回调,允许逻辑解码插件支持两阶段提交。

回调函数

描述

filter_prepare_cb

允许插件根据PREPARE TRANSACTION命令中使用的GID过滤Prepare时不需要解码的事务

begin_prepare_cb

Prepare事务的开始

prepare_cb

PREPARE TRANSACTION命令被解码时调用

commit_prepared_cb

COMMIT PREPARED命令解码时调用

rollback_prepared_cb

ROLLBACK PREPARED命令解码时调用

详细信息请参考:


https://github.com/postgres/postgres/commit/0aa8a01d04c8fe200b7a106878eebc3d0af9105c


插件修改


test_decoding

该插件是一个逻辑解码输出插件,作为一个示例帮助用户开发自己的逻辑解码插件。test_decoding通过逻辑解码机制接收WAL,并将其解码为所执行操作的文本表示。

它被修改为能够在prepare时使用新的两阶段回调函数和解码事务


APIs的修改


pg_create_logical_replication_slot()

API添加了新的选项指定slot是否支持两阶段提交。输出插件可以使用带有两阶段选项的复制槽以支持两阶段提交。

pg_create_logical_replication_slot(slot_name name, plugin name [, temporary boolean, two_phase boolean ] )

详细请参考:

https://github.com/postgres/postgres/commit/19890a064ebf53dedcefed0d8339ed3d449b06e6


案例


看下怎么检测两阶段提交的事务解码输出:

1) 创建一个复制槽

使用test_decoding作为输出插件,传入true,这样slot支持两阶段提交解码。

    postgres=# SELECT * FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding', false, true);
        slot_name    |   lsn
    -----------------+-----------
     regression_slot | 0/16B1970
    (1 row)  

    2) 创建一个表



    postgres=# CREATE TABLE data(id serial primary key, data text);
    CREATE TABLE

    3) 检测prepare事务和commit事务的解码输出内容


    postgres=# BEGIN;
    postgres=*# INSERT INTO data(data) VALUES('5');
    postgres=*# PREPARE TRANSACTION 'test_prepared1';
    postgres=# SELECT * FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL);
        lsn    | xid |          data
    -----------+-----+---------------------------------------------------------
     0/1689DC0 | 529 | BEGIN 529
     0/1689DC0 | 529 | table public.data: INSERT: id[integer]:3 data[text]:'5'
     0/1689FC0 | 529 | PREPARE TRANSACTION 'test_prepared1', txid 529
    (3 rows)
    postgres=# COMMIT PREPARED 'test_prepared1';
    postgres=# select * from pg_logical_slot_get_changes('regression_slot', NULL, NULL);
        lsn    | xid |       data
    -----------+-----+--------------------------------------------
     0/168A060 | 529 | COMMIT PREPARED 'test_prepared1', txid 529
    (4 rows)
    postgres=# select * from data;
     id | data
    ----+------
      1 |  5
    (1 row)

    未来


    PG14对此功能的更改,有了解码器端的基础架构,允许在prepare时解码两阶段提交。我们还修改了test_decoding插件以利用此基础架构。

    下一步就是把对两阶段的支持实现到PG内部最大的逻辑解码插件--pgoutput插件中。这个插件支持逻辑复制的PUBLISHER/SUBSCRIBER 模式。他是逻辑复制中使用最广泛的插件。富士通OSS团队正在和开源社区合作,以在PG15中添加此功能。

    对于分布式数据库中的两阶段事务,PG也需要支持:备机通知主机PREPARE失败了,发起回滚。这种反馈机制在PG中不支持,是未来改进的方向之一。


    原文


    https://www.postgresql.fastware.com/blog/logical-decoding-of-two-phase-commits

    相关实践学习
    使用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
    |
    1月前
    |
    存储 关系型数据库 数据库
    【赵渝强老师】PostgreSQL的逻辑存储结构
    PostgreSQL的逻辑存储结构包括数据库集群、数据库、表空间、段、区、块等。每个对象都有唯一的对象标识符OID,并存储于相应的系统目录表中。集群由单个服务器实例管理,包含多个数据库、用户及对象。表空间是数据库的逻辑存储单元,用于组织逻辑相关的数据结构。段是分配给表、索引等逻辑结构的空间集合,区是段的基本组成单位,而块则是最小的逻辑存储单位。
    【赵渝强老师】PostgreSQL的逻辑存储结构
    |
    4月前
    |
    安全 关系型数据库 MySQL
    揭秘MySQL海量数据迁移终极秘籍:从逻辑备份到物理复制,解锁大数据迁移的高效与安全之道
    【8月更文挑战第2天】MySQL数据量很大的数据库迁移最优方案
    804 17
    |
    4月前
    |
    SQL 关系型数据库 数据库
    [postgresql]逻辑备份与还原
    [postgresql]逻辑备份与还原
    |
    6月前
    |
    存储 关系型数据库 MySQL
    MySQL数据库——InnoDB引擎-逻辑存储结构(表空间、段、区、页、行)
    MySQL数据库——InnoDB引擎-逻辑存储结构(表空间、段、区、页、行)
    142 7
    |
    6月前
    |
    存储 关系型数据库 MySQL
    【MySQL技术内幕】4.2-InnoDB逻辑存储结构
    【MySQL技术内幕】4.2-InnoDB逻辑存储结构
    56 0
    |
    7月前
    |
    SQL 存储 缓存
    mysql 逻辑架构
    mysql 逻辑架构
    |
    7月前
    |
    SQL 关系型数据库 分布式数据库
    PolarDB for PostgreSQL逻辑复制问题之逻辑复制冲突如何解决
    PolarDB for PostgreSQL是基于PostgreSQL开发的一款云原生关系型数据库服务,它提供了高性能、高可用性和弹性扩展的特性;本合集将围绕PolarDB(pg)的部署、管理和优化提供指导,以及常见问题的排查和解决办法。