KingbaseES XA 分布式事务
在分布式系统中,各个节点(或者事务参与方)之间在物理上相互独立,各节点之间无法确切地知道其它节点中的事务执行情况,所以多节点之间很难保证ACID,尤其是原子性。如果是单节点的事务,由于存在事务机制,可以保证其数据操作的ACID特性。如果要实现分布式系统事务的原子性,必须保证所有节点的数据写操作,要不全部都执行,要么所有节点全部都不执行。但是,一个节点在执行本地事务的时候无法知道其它节点的本地事务的执行结果,所以它就不知道本次事务到底应该commit还是 rollback。这就需要引入一个“协调者”的组件来统一调度所有分布式节点的执行。
两阶段提交原理
二阶段提交的算法思路可以概括为:协调者询问参与者是否准备好了提交,并根据所有参与者的反馈情况决定向所有参与者发送commit或者rollback指令。
所谓的两个阶段是指:
- 准备阶段 又称投票阶段。在这一阶段,协调者询问所有参与者是否准备好提交,参与者如果已经准备好提交则回复Prepared,否则回复Non-Prepared。
- 提交阶段 又称执行阶段。协调者如果在上一阶段收到所有参与者回复的Prepared,则在此阶段向所有参与者发送commit指令,所有参与者立即执行commit操作;否则协调者向所有参与者发送rollback指令,参与者立即执行rollback操作。
KingbaseES两阶段提交
- prepare transaction transaction_id:prepare transaction 为当前事务的两阶段提交做准备。 在命令之后,事务就不再和当前会话关联了,它的状态完全保存在磁盘上, 它提交成功有非常高的可能性,即使是在请求提交之前数据库发生了崩溃也如此。这条命令必须在一个用BEGIN显式开始的事务块里面使用。
- commit prepared transaction_id:提交已进入准备阶段的ID为transaction_id的事务
- rollback prepared transaction_id:回滚已进入准备阶段的ID为transaction_id的事务
使用例子
设置数据库参数,确保 max_prepared_transactions 大于 0
test=# alter system set max_prepared_transactions=100; ALTER SYSTEM
会话A:在prepare 后,全局事务ID 会保存到本地表中,即使数据库异常宕机也能恢复事务。
test=# begin; BEGIN test=# create table tt1(id integer); CREATE TABLE test=# PREPARE TRANSACTION 'the first prepared transaction'; PREPARE TRANSACTION test=# \q
会话B:
test=# SELECT * FROM pg_prepared_xacts; transaction | gid | prepared | owner | database -------------+--------------------------------+-------------------------------+--------+---------- 41207 | the first prepared transaction | 2022-04-27 17:33:12.411756+08 | system | test (1 row) test=# ROLLBACK PREPARED 'the first prepared transaction'; ROLLBACK PREPARED
test=# SELECT * FROM pg_prepared_xacts; transaction | gid | prepared | owner | database -------------+-----+----------+-------+---------- (0 rows)
注意:在运行 prepare transaction时,本地的事务就已经结束,后续的commit or rollback 不必在事务块里执行。
test=# begin; BEGIN test=# create table tt1(id integer); CREATE TABLE test=# PREPARE TRANSACTION 'the first prepared transaction'; PREPARE TRANSACTION test=# rollback; WARNING: there is no transaction in progress ROLLBACK
两阶段提交注意事项
- PREPARE TRANSACTION transaction_id 命令后,事务状态完全保存在磁盘上。
- PREPARE TRANSACTION transaction_id 命令后,事务就不再和当前会话关联,因此当前session可继续执行其它事务。
- COMMIT PREPARED 和 ROLLBACK PREPARED 可在任何会话中执行,而并不要求在提交 prepare transaction 的会话中执行。
- 不允许对那些执行了涉及临时表或者是创建了带WITH HOLD游标的事务进行PREPARE。 这些特性和当前会话绑定得实在是太紧密了,因此在一个准备好的事务里没什么可用的。
- 如果事务用SET修改了运行时参数,这些效果在PREPARE TRANSACTION之后保留,并且不会被任何以后的COMMIT PREPARED或ROLLBACK PREPARED所影响,因为SET的生效范围是当前session。
- 从性能的角度来看,把一个事务长时间停在准备好的状态是不明智的,因为它会影响VACUUM回收存储的能力。
- 已准备好的事务会继续持有它们获得的锁,直到该事务被commit或者rollback。所以如果已进入准备阶段的事务一直不被处理,其它事务可能会因为获取不到锁而被block或者失败。
KINGBASE研究院