全面概述TiKV乐观事务2PC机制 分布式事务实现原理

网友投稿 550 2024-03-05



前言

关于分布式事务基本原理,相关资料比较多写的比较全面,这里罗列一些需要了解的基本原理文章:

全面概述TiKV乐观事务2PC机制 分布式事务实现原理

首先我们需要了解传统的分布式事务模型,2PC3PC

https://developer.aliyun.com/article/1199985

接下来我们需要了解一下,GooglePercolator 事务模型:

https://tikv.org/deep-dive/distributed-transaction/percolator/

https://zhuanlan.zhihu.com/p/261115166

我们看一下 TIKV 实现的 Percolator 事务模型:

https://cn.pingcap.com/blog/tidb-transaction-model/

https://cn.pingcap.com/blog/tikv-source-code-reading-12/

最后,TIKVPercolator 事务模型下数据的读取:

https://cn.pingcap.com/blog/tikv-source-code-reading-13/

2PC 与 3PC

对于传统的事务模型来说,一般都有两种角色,coordinator (协调者) 和 participant (参与者)

2PC 有个无法避免的 case

假如事务参与者 participant 有 3 个,分别是 p1, p2, p3,协调者有一个c1

事务过程中,p2p3 已经 Prepare 成功,事务状态有以下几种可能:

p1 还未收到 Prepare,当前事务总状态为 Prepare

或者, p1Prepare 成功,还未收到协调者 Commit/Rollback 请求,当前事务总状态为 Prepare

或者, p1Prepare 失败,协调者 c1p1 发送了 Rollback 请求,p1 返回 ok,当前事务总状态为 Rollback

或者, p1Prepare 成功, 协调者 c1p1 发送 Commit 请求,p1 返回 ok,当前事务总状态为 Commit

协调者 c1 与事务参与者 p1 全部 Down

协调者 c2 被启动,这个时候,c2 查询 p2p3 的状态为 Prepare,但是事务总状态完全无法肯定,Prepare/Commit/Rollback 均有可能,只能等待 p1 服务或者 c1 的故障恢复后才能完全确定事务状态。

3PC 通过在 PrepareCommit 中间添加一个 PreCommit 状态来解决这个问题。

c1p1down 的状态下,新启动的 c2 查询 p2p3 的当前状态就可以确定当前事务状态

假如 p2p3 都是 Prepare 状态的话,

p1 的状态可能是 Prepare 或者 PreCommit,不可能是 Commit 或者 Rollback

所以事务都不算生效,可以放心回滚事务

假如 p2p3 分别是 PreCommitPrepare状态的话

p1 的状态可能是 Prepare 或者 PreCommit,不可能是 Commit 或者 Rollback

所以事务都不算生效,可以放心的回滚事务

假如 p2p3 都是 PreCommit 状态的话,说明 p1p2p3Prepare 成功,

p1 的状态可能是 Prepare、PreCommit 或者 Commit,不可能是 Rollback

由于 p1 可能已经提交,因此需要提交事务

然而 3PC 状态下,多了一次交互,性能肯定会有所下降,而且也无法解决网络分区的问题:

假如事务参与者 participant 有 3 个,分别是 p1, p2, p3,协调者有一个 c1

p1, p2 已经 Precommit 成功,p3 还未 Precommit, 这时候发生网络分区状况,p3 被单独隔离到一个网络分区

p1, p2选举出 coordinatorc2c2 查询 p1p2 状态是 Precommit 后,提交了事务

p3 选举出 c3c3 查询 p3 状态为 Prepare 状态,回滚了事务

事务的状态存在不一致的问题

Percolator 事务模型

对于 Percolator 事务模型来说,已经不存在传统意义的 coordinator (协调者) 和 participant (参与者),所有的事务状态都存储在参与者中。

也可以说 coordinator 不再存储 PrewriteCommitRollback 状态,所有的状态都存储在参与者 participant 中。

Percolator 实现分布式事务主要基于3个实体:ClientTSOBigTable

Client 是事务的发起者和协调者

TSO 为分布式服务器提供一个精确的,严格单调递增的时间戳服务。

BigTableGoogle 实现的一个分布式存储

Percolator 事务模型是 2PC 的一种实现方式,为了解决 2PC 的容灾问题,参与者 participant 会将 PrepareCommit 等状态通过分布式协议 RAFTPaxos 进行分布式存储。确保参与者 participant 即使 Fail Down,恢复回来以后事务状态不会丢失。

还是以之前的例子:

假如事务参与者 participant 有 3 个,分别是 p1, p2, p3,协调者有一个 c1

事务过程中,p2p3 已经 Prewrite 成功

p1 还未收到 Prewrite,当前事务总状态为 Prewrite

或者, p1Prewrite 成功,还未收到协调者 Commit/Rollback 请求,当前事务总状态为 Prewrite

或者, p1 Prewrite 成功,协调者 c1p1 发送 Commit 请求,p1 通过 RAFT 协议同步事务状态后, 当前事务总状态为 Commit

或者, p1 Prewrite 失败,协调者 c1p1 发送了 Rollback 请求,p1 通过 RAFT 协议同步事务状态后,当前事务总状态为 Rollback

协调者 c1 与事务参与者 p1 全部 Down

协调者 c2 被启动,参与者 p1 虽然 Down,但是会有容灾节点 p1-1 被启动。c2 查询 p1-1节点的存储状态

如果 p1-1 的状态为 None,那么可以放心的 Rollback

如果 p1-1 的状态为 Prewrite,那么可以放心的 Rollback

如果 p1-1 的状态为 Rollback,那么可以放心的 Rollback

如果 p1-1 的状态为 Commit, 那么必须进行 Commit

2PC 中,最关键的莫过于 Commit Point(提交点)。

因为在 Commit Point 之前,事务都不算生效,并且随时可以回滚。而一旦过了 Commit Point,事务必须生效,哪怕是发生了网络分区、机器故障,一旦恢复都必须继续下去。

事务的流程

由于采用的是乐观事务模型,写入会缓存到一个 buffer 中,直到最终提交时数据才会被写入到 TiKV;

而一个事务又应当能够读取到自己进行的写操作,因而一个事务中的读操作需要首先尝试读自己的 buffer,如果没有的话才会读取 TiKV。

当我们开始一个事务、进行一系列读写操作、并最终提交时,在 TiKV 及其客户端中对应发生的事情如下表所示:

Percolator 事务模型举例:

Let’s see the example from the paper of Percolator. Assume we are writing two rows in a single transaction. At first, the data looks like this:

This table shows Bob and Joe’s balance. Now Bob wants to transfer his $7 to Joe’s account. The first step isPrewrite:

Get the start_ts of the transaction. In our example, it’s 7.

For each row involved in this transaction, put a lock in the lock column, and write the data to the datacolumn. One of the locks will be chosen as the primary lock.

After Prewrite, our data looks like this:

Then Commit:

Get the commit_ts, in our case, 8.

Commit the primary: Remove the primary lock and write the commit record to the write column.

Commit all secondary locks to complete the writing process.

TIKV 事务接口概览

这里大致写一下乐观事务中,2PC 的大致流程,各个接口的详细逻辑与样例场景可以参考后续文章。

Prewrite 接口

2PC 的第一阶段,预提交。目的是将事务涉及的多个 KEY-VALUE 写入 default_cf,同时将在 lock_cf 上加锁

主要流程

检查在 lock_cf 中没有记录,也就是没有锁

检查在 write_cf 中没有大于等于当前事务 start_ts 的记录

KEY-VALUE 写入 default_cf

lock 信息写入 lock_cf

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:修复Etcd API未授权访问漏洞的策略
下一篇:全面测试TiDB性能 使用HyBench实战报告
相关文章