Percolator 事务模型
Percolator 协议是一个 2PC 协议,TiDB 、 CockroachDB 等都使用 Percolator 协议来做事务
Percolator 协议把数据附加额外 2 个信息以及版本的概念
如下,一个数据 A ,类似以下方式表达:
版本 | data | lock | write |
---|---|---|---|
5 | 4 | ||
4 | hello |
这时, A 的值为 hello ( write 字段指示了当前数据是哪个版本)
lock write 字段都是控制字段,其本身无版本概念,以上 table 方式表达,主要用于描述 2PC 时序过程:
- lock 字段无需版本,用于事务过程。它是多读单写的
- write 字段无需版本,它实际 2 个信息组合
- 本次事务,2PC 过程中指向的版本
- 上次事务完成,指向的版本
Percolator 的 2PC 过程
比如有 A B 数据:
版本 | data | lock | write |
---|---|---|---|
5 | 4 | ||
4 | hello |
版本 | data | lock | write |
---|---|---|---|
1 | world | 1 |
要把 A B 数据同时设置为大写: HELLO WORLD ,则按以下处理:
第一阶段:
版本 | data | lock | write |
---|---|---|---|
6 | HELLO | main lock | |
5 | 4 | ||
4 | hello |
版本 | data | lock | write |
---|---|---|---|
2 | WORLD | link A.lock | |
1 | world | 1 |
任何一方写失败等,做回滚处理
- 除 main lock 的数据,其他的可并发先删
- 最后删除 main lock 的数据
也可能是异常超时处理(最后讲)
- 回滚处理也要考虑超时后的新 lock ,这种是乱序的误删(细节)
第二阶段:
提交阶段:
- 从 main lock 的 A 开始提交(这步提交成功,即事务已经成功)
- 其他非 main lock 数据,可并发提交(提交失败也没问题)
版本 | data | lock | write |
---|---|---|---|
7 | 6 | ||
6 | HELLO | ||
5 | 4 | ||
4 | hello |
版本 | data | lock | write |
---|---|---|---|
3 | 2 | ||
2 | WORLD | ||
1 | world | 1 |
这里主要解释下,为啥这个阶段,只要 main lock 的提交成功即可
考察以下提交情况, B 数据提交异常了:
版本 | data | lock | write |
---|---|---|---|
7 | 6 | ||
6 | HELLO | ||
5 | 4 | ||
4 | hello |
版本 | data | lock | write |
---|---|---|---|
2 | WORLD | link A.lock | |
1 | world | 1 |
在读 B 数据时
- 发现 lock 还是锁着的,则查看 main lock 的情况
- 发现 main lock 锁已经无
- 读操作,读最新版本 2 的数据
- 写操作(第一阶段), data lock 正常写,附带 write 字段写 2
其他各种情况:正常、各种异常,都以 main lock 的情况,进而读取正确数据。不再一一列举
Percolator 的超时处理
根据上述介绍,可以发现, Percolator 对于异常是一种 lazy (懒)处理
对于超时的情况,仅仅根据版本号,其实是不够的,因为数据可能在多台主机上
比较正规(严谨)的是版本号用全局时间戳代替,即版本号也是时间戳
这样就可以判断锁是否超时了,进而可以判断 main lock 是否超时失效
- 典型的,第一阶段 main lock 了,然后没法进行第二阶段提交
另外,对于 main lock 字段内容,最好附带所有子锁信息,这样 lazy (懒)处理 main lock 字段时:
- 先解锁其他 lock
- 最后解锁自己