Percolator 分布式事务原理

简介

Percolator 提供了跨行、跨表的、基于快照隔离的 ACID 事务。

结构

Percolator 为了实现分布式事务,抽象了三个列。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

Data
作用:
具体存储数据集
映射关系: {key, start_ts} => {value}
key: 真实的 key
start_ts: 事务开始时间戳
value: 真实的数据值
Lock
作用:
事务执行中产生的锁
事务开启时获取锁
事务提交时释放锁
映射关系: {key, start_ts} => {primary_key, lock_type,..etc}
key: 数据的 key
start_ts: 事务开始时间戳
primary_key: 该事务的 primary_key 的引用. primary_key 是在事务执行时,从待修改的 keys 中选择一个作为 primary_key, 其余的则作为 secondary key.
Write
作用:
已提交事务信息,存储提交时间戳
write 列记录着 key 的提交记录,当客户端读取一个 key 时,会从 write 列中读取 key 所对应数据在 data 列中的存储位置,然后从 data 列中读取真正的数据。
映射关系:
{key, commit_ts} => {start_ts}
key: 数据的 key
commit_ts: 事务的提交时间戳
start_ts: 事务开始时间戳, 指向该数据在 data 中的实际存储位置

流程

写流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
1.Prewrite阶段
1.从 TSO 获取 start_ts
2.尝试对所有被写元素加锁, percolator 为所有需要写的 key 选出一个作为 primary, 其余的作为 secondary
1.每个 key 处理流程如下,中间出现失败,则 Prewrite 失败(先 Prewrite primary,成功后再 Prewrite secondaries)
1.检查 write-write 冲突
若 write 列当前 key 的最新数据 commit_ts 大于等于 start_ts,存在更新版本的已提交事务,失败,直接 Abort 整个事务
2.检查 lock 列中该写入操作是否被上锁
若 key 已被上锁,失败,直接 Abort 整个事务
2.Lock 列写入 lock(start_ts, key, primary) 为当前 key 加锁
若当前 key 被选为 primary, 则标记为 primary;
若为 secondary, 则标明指向 primary 的信息
3.Data 列写入 data(key, start_ts, value)
由于此时 write 列尚未写入,因此数据对其它事务不可见
2.Commit阶段
1.从 TSO 获取 commit_ts
2.依次对各个 key 进行提交
1.先 Commit primary , 如果失败则 Abort 事务
检测 lock 列 primary 对应的锁是否存在,如果锁已经被其它事务清理(触发 failover 可能导致该情况),则失败。
写入 Write 列: commit_ts -> data 列的 start_ts
释放锁:删除 Lock 列当前 key
2.再异步的进行 Commit secondary
Commit seconary 无需检测 lock 列锁是否还存在,一定不会失败
写入 Write 列: commit_ts -> data 列的 start_ts
释放锁:删除 Lock 列当前 key

读流程

1
2
3
4
5
1.Get 操作首先检查 [0, start_ts] 时间区间内 Lock 是否存在
1.若存在,则返回错误
2.如果不存在有冲突的 Lock
1.获取在 Write 中合法的最新提交记录指向的在 Data 中的位置
2.从 Data 中获取到相应的数据并返回

异常处理流程(异步清理锁)

若 Client 在 Commit 一个事务时,出现了异常,Prewrite 时产生的锁会被留下。为避免将新事务 hang 住,Percolator 必须清理这些锁。

Percolator 用 lazy 方式处理这些锁:
当事务 A 在执行时,发现事务 B 造成的锁冲突,事务 A 将决定事务 B 是否失败,以及清理事务 B 的那些锁。

1
2
3
4
5
6
7
根据key 从 Lock 列查询 primary 锁
1.若 primary lock 存在
事务B被 roll back
因为我们总是最先提交 primary, 所以 primary 未被提交时,可以安全地执行回滚
2.若 primary lock 不存在
说明 primary lock 已被 WRITE 所替代,也就是说该事务已被提交,事务需要 roll forword
因为:在 B 提交 commit 之前, 它会先确保其 primary lock 被 write record 所替代(即往 primary 的 write 写提交数据,并删除对应的 lock)。

举例

// TODO

参考

Percolator论文