Google Percolator 分布式事务模型

最近对 TiDB 特别感兴趣,稍微研究了一下他们应用到的 Percolator 事务模型。

BigTable

BigTable 是一个分布式, 多维, 映射表。本质上说,BigTable 是一个键值(key-value)映射。主要有三个维度,分别是行、列、时间戳。

BigTable 存储映射为:(row:string, column:string, time:int64)→string

从存储的映射时间戳维度不难看出,BigTable 是支持可以多版本控制(MVCC)的。

BigTable支持单行的事务,可以保证一行多列的 ACID 特性。明显单行事务对于一个现代化系统来说略显不足,Percolator 借助 BigTable 实现了多行的分布式事务。

Percolator事务流程

Percolator事务分为两个阶段:预写(Pre-write)和提交(Commit),本质上相当于一个加强的2PC。

需要应用 Percolator 事务的 BigTable 的表中,都需要加入下面两个列族:

  • L列: 也就是 Lock 列,需要记录该行数据的锁信息
  • W列:也就是 Write 列,需要记录该行数据 Last Committed 的数据版本,用来做版本控制。

为什么说列族呢?首先 BigTable 一行中每一个列是允许存储多个时间版本的数据,方便实现例如:读已提交、读未提交、可重复度、序列化等事务隔离级别。

预写(Pre-write)

这里引用 Percolator 原文的一个例子,我们需要将 Bob 的账户中的 7 元转给 Joe 的账户中。

初始状态

首先我们看 bal:write 列,此时 Bob 和 Joe 最新的一个时间戳版本 6 都指向各自的 data@5,说明在 6 这个时间戳版本中: Bob 的账户余额有 10 元,Joe 的账户余额有 5 元。那么持有大于时间戳 6 的事务进行读未提交的时候,可以读到时间戳版本为 5 的 bal:data。

初始状态

Pre-Write

在 Percolator 里面,首先需要把在同一个事务里面多个 Key 随机选出一个 Primary Key 和多个 Secondary Key。所在数据行分别称为 Primary Row 和 Secondary Row。

首先进行的是 Primary Row 的 Pre-Write 操作:

  1. TSO 拿到当前时间戳 start_ts = 7
  2. 检查 <Bob bal:write> 列,如果有大于 7 的数据版本则提交失败,有则说明其他事务已经写入数据(写冲突),没有则继续处理
  3. 检查 <Bob bal:lock> 列,如果有小于 7 的数据版本锁则提交失败,有则说明其他事务已经占用数据,没有则加锁继续处理
  4. 设置 <Bob bal:data 7>3

此时已经完成了 Primary RowPre-Write 操作。2~4 步骤需要在同一个 bigtable 事务里面进行原子操作。

可能会有疑问为什么在此时已经把数据列 <Bob bal:data 7> 写上了?其实由于 <Bob bal:write> 列最新的数据还未写入,在其他事务看来,这属于未提交内容,其他事务可以根据事务隔离级别有选择读取 <Bob bal:data> 的时间戳版本数据。

Primary Row Pre-Write

那么针对多个 Secondary Row 的 Pre-Write 也与 Primary Row 类似,只是锁的记录需要指向 Primary Row 的锁。这样子实现了去中心化的锁管理,把Secondary Lock 与 Primary Lock 关联了起来。

Secondary Row 的 Pre-Write 操作:

  1. 拿到 Primary Row 的 Pre-Write 中获得的时间戳 start_ts = 7
  2. 检查 <Joe bal:write> 列,如果有大于 7 的数据版本则提交失败,有则说明其他事务已经写入数据(写冲突),没有则继续处理。
  3. 检查 <Joe bal:lock> 列,如果有小于 7 的数据版本锁则提交失败,有则说明其他事务已经占用数据,没有则加锁继续处理。
  4. 设置 <Joebal:data 7>9

多个 key 也是类似,在实际应用场景中,可以异步对多个 key 加锁,加快速度。

Secondary Row Pre-Write

自此预写(Pre-write)过程已经完成了!

提交(Commit)

目前为止已经把想要修改到的数据已经加好锁了,接下来需要进行 Commit 操作。

首先进行的是 Primary Row 的 Commit 操作:

  1. TSO 拿到当前时间戳 commit_ts = 8
  2. 检查 <Bob bal:lock> 列,看锁是否存在,不存在则可能已经被清除了,取消事务;存在则继续。
  3. commit_ts 为版本号,指向 bal:write 列的 start_ts 对应数据版本。也就是把 <Bob bal:write 8> 设置为 data@7。此步骤完成后,写入的数据版本已经生效,也就是对读已提交事务可见了。
  4. 删除锁信息,让其可写。

1~3步骤需要在一个 bigtable 事务里面进行原子操作。

Primary Row Commit

Secondary Row 的 Commit 操作与 Primary Row 的类似。

Secondary Row Commit

随便聊点

事务隔离级别

与传统数据库事务隔离级别(读已提交、读未提交、可重复度、可序列化)相比,Percolator 提供了快照隔离级别。

优点:

  • 保证事务中的读操作读到对应数据版本,避免产生不可重复读的问题。
  • 保证多事务中的写操作不会更新到同一条记录。

缺点也比较明显:

  • 写倾斜(Write)问题。
  • 乐观事务会产生写热点问题。

锁管理

Percolator 抛弃中心锁管理,把锁信息分散数据当中。通过区分 Primary 和 Secondary,巧妙的设置了一个标志,后续的异常处理都可以通过这个标签来进行。

锁有可能有以下异常:

  1. Prewrite 中断,还进行 primary lock 或者写 secondary lock 到一半系统崩溃。
  2. Commit 中断,未进行 primary commit 或者 primary commit 到一半系统崩溃。

此时就需要用到锁清理,锁清理不需要另外开任务去管理和回收。只需要在读操作的时候遇到锁的时候特殊处理即可。减轻了锁维护的成本,也简化了整个锁的管理模型。

那是怎么处理的呢?

每个事务开启的时候都会从 TSO 获取事务开始时间 start_ts ,通过判断某一行数据的 lock 列是否在 (0, start_ts] 范围内为两种情况:

  1. 不在;说明此锁可读:首先读取 write 列小于 start_ts 的最大的数据,然后去读 data 列。
  2. 在;说明此锁不可读,此时如果按照锁可读情况处理的话,可能会产生读未提交的问题。

在锁不可读的情况下,也不可能无休止等待,在一定的延迟后,会进行以下操作:

  1. 遇到 primary lock 还在,可以进行锁清除。
  2. 遇到 secondary lock 还在,检查 primary lock。
    1. primary lock 还在,事务 commit 失败,回滚事务
    2. primary lock 不在,事务 commit 已经成功了,进行事务前滚(没错,就是前滚)。

Percolator 缺点

  1. 由于依赖 TSO,会发现网络交互比较多;TiDB 团队针对退出了 Async Commit,可以减少网络交互。
  2. 乐观锁存在热点读写回滚风暴问题;TiDB 团队针对此推出了悲观事务模型。
  3. 依赖读清理锁,会有写冲突问题。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,340评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,762评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,329评论 0 329
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,678评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,583评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,995评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,493评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,145评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,293评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,250评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,267评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,973评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,556评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,648评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,873评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,257评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,809评论 2 339

推荐阅读更多精彩内容

  • 前言 前段时间忙双11忙到废寝忘食,这期间又被各种奇奇怪怪的小病折腾了半个多月,整个人状态不是很好,博客也连续吃灰...
    LittleMagic阅读 2,942评论 7 9
  • 在进入正题之前,先来思考下跨节点的数据如何实现同进退(ACID),如果对分布式事务本身有一定了解可跳过这里。如图:...
    Eshin_Ye阅读 2,622评论 0 1
  • --- Percolator 事务隔离级别SI Percolator提供跨表、跨行的分布式事务,隔离级别为快照隔离...
    CodeLess阅读 944评论 0 0
  • 我是黑夜里大雨纷飞的人啊 1 “又到一年六月,有人笑有人哭,有人欢乐有人忧愁,有人惊喜有人失落,有的觉得收获满满有...
    陌忘宇阅读 8,518评论 28 53
  • 人工智能是什么?什么是人工智能?人工智能是未来发展的必然趋势吗?以后人工智能技术真的能达到电影里机器人的智能水平吗...
    ZLLZ阅读 3,759评论 0 5