PostgreSQL , unknown 事务
数据库的事务是原子操作,要么成功,要么失败。但是实际上在客户端的视角,可能有第三种状态:unknown 状态。
当客户端提交事务结束(rollback , commit , prepare xact , rollback pxact , commit pxact)的请求后,数据库收到请求,数据库可能执行失败,也可能执行成功,不管怎样都要写对于的 WAL 日志,还有 CLOG,然后数据库要将执行结果返回给客户端 ACK。
这里存在几种可能,导致客户端不知道执行到底怎么样了?
收到客户端请求后,数据库没有返回任何 ACK 给客户端,客户端对这次请求很茫然,它只能人为数据库处于 UNKNOWN 的状态。
unknown 事务,就是客户端没有收到 commit/rollback ACK 的事务。不知道是成功还是失败。
多节点 (quorum based sync replication) 与单节点都可能出现 UNKNOWN 事务,效果、形态一致。
如何处理 unknown 事务呢?
unknown 事务分为以下几种情况.
rollback , commit , prepare xact , rollback pxact , commit pxact 几种情况的 unknown 处理方法:
1、两阶段解决 unknown 状态问题
prepare 阶段 unknown, 切换 leader 后,客户端通过 pg_prepared_xacts 视图检查 prepare xact 状态,如果没有 prepare xact 则说明失败了,那么整个事务重新发起即可。如果 prepare xact 存在,说明 prepare xact 成功了。
commit or rollback prepare xact 阶段 unknown, 切换后检查 prepare xact 状态,存在则重试 commit or rollback prepare xact。不存在则说明已经成功(我们认为 2PC 是一定成功的),无须处理。
2、非两阶段事务,rollback unknown 无须处理,rollback 失败或成功对于客户端来说结果是一样的。因为不管怎样都会回滚掉,这是数据库原子性保障的。
3、非两阶段事务,commit unknown 处理,极度严谨的场景,程序可以设计事务状态可回溯,例如:
事务开始时,记录事务号或唯一流水号,事务号在数据库中是一个唯一的流水,可以根据事务号查询它的状态,比如 postgresql。
但是并不是所有数据库都有这种接口,比如非物理流式复制的数据库,则可以在事务中增加全局唯一流水号来查看事务是否提交。这里利用了事务的原子特性,既要么全成功要么全失败。可以举个使用例子。
使用业务流水实现事务状态判断的例子:
- begin;生成唯一业务流水ID,
- 写入到某个流水表,同时在程序或其他数据库中记录这个流水号,备查。执行事务提交事务;
- --出现unknown
- 通过唯一业务流水ID,查询数据库中是否存在这条记录。如果不存在,说明事务提交失败。如果存在,说明事务提交成功。(因为数据库的事务是原子操作)
来源: https://yq.aliyun.com/articles/126903