Graph + AI 中国峰会火热报名中,点击探索图分析更多可能! 了解详情
写点什么

CockroachDB 是如何实现分布式原子事务的?

2015 年 9 月 11 日

作为一个分布式数据库, CockroachDB 有一个鲜明的特性,就是支持 ACID 事务。近日, Cockroach Labs 团队成员 Matt Tracy撰文介绍了CockroachDB 如何在不使用锁的情况下实现原子事务。

CockroachDB 的原子事务策略总共包含如下五个基本步骤。

1.“开关(Switch)”:CockroachDB 事务记录

在开始一个事务之前,写进程首先会创建一个包含如下字段的事务记录

  • 事务唯一标识 UUID
  • 事务当前状态,可以有三种取值,其中 PENDING 为初始状态,ABORTED 或 COMMITTED 为终止状态
  • 一个 Cockroach K/V 键,指定“开关”在分布式数据存储中的位置

写进程会使用一个专门的 CockroachDB 命令BeginTransaction()存储事务记录。事务记录或者开关不能并行访问,只能严格按照顺序进行读 / 写操作。事务记录的 PENDING 或 ABORTED 状态对应开关的“off”状态,COMMITTED 对应“on”。

2.“暂存(Stage)”:Write Intent

写进程会准备好多个数据库变更,但并不会覆盖任何现有值。为了暂存一个事务中的变更,CockroachDB 使用了一个名为Write Intent的结构。任何时候,一个值作为一个事务的一部分写入到一个键,它都会生成一个 Write Intent。Write Intent 结构中还包含指示事务记录存储位置的键。

另外,还有一个规则,就是任何键都有且仅有一个 Write Intent(Matt 在此并未考虑事务并发)。

3.“过滤(Filter)”:读取 Write Intent

对于任何有暂存值的键,在读取时都必须检查事务开关的状态。如果开关状态为“off”,则返回该键的原值;如果开关状态为“on”,则返回暂存值。也就是说,如果一个键有暂存值,那么对该键的所有读取操作都必须使用开关状态过滤,具体步骤如下:

  1. 如果现有 Write Intent 对应的事务记录仍然处于 PENDING 状态,则将其置为 ABORTED 状态。
  2. “清除(cleanup)”现有的 Write Intent。
  3. 返回该键的普通值(即不是 Write Intent)。如果之前的事务已经 COMMITTED,那么清除操作会将暂存值升级为普通值;否者,返回该键的原值。

4.“按开关(Flip)”:提交事务

当写进程准备好事务中的所有变更后,它会打开开关(即将事务记录更新成 COMMITTED,开关状态置为“on”)。事务生成的所有 Write Intent 都会立即生效;之后发生的任何读取操作在看到事务已经提交后都会返回 Write Intent 中的暂存值。

另外,将事务记录的状态置为 ABORTED 可以终止事务,之后发生的读取操作将忽略这个事务生成的 Write Intent。

5.“解除暂存(Unstage)”:清除 Write Intent

前面 4 个步骤已经可以提供原子事务;但是,第 2 步开销很大,因此,在事务完成后,CockroachDB 会尽快清除暂存值。如果事务成功,那么暂存值会取代原值;如果失败,那么暂存值会被丢弃。总之,如果一个键没有 Write Intent,那么读取操作就无需过滤,也就可以以适当的分布式方式完成。需要注意的是,解除暂存是异步的,并不需要在事务 COMMITTED 之前完成。

网友 RJ Ryan 对 Matt 所谓的“无锁(lockless)”提出了质疑。他认为,开关和 Write Intent 看起来跟锁类似。对此,Matt 答复说:

这篇博文谈到“锁”时,是指数据库记录锁,一个修改数据库某个部分的独占性许可。

开关(或事务记录)并不是一个数据库锁,因为它没有阻止其它数据库操作进行……对开关本身的操作会按顺序进行……但它不会阻止任何操作执行。

Write Intent 也不是数据库锁,因为它不会保证键的独占性访问;相反,它允许一个事务“发现”其它正在访问相同键的并发事务。当这种情况出现时,其中一个事务必须终止;不过,它并没有像锁一样采用“先来先服务”的策略,而是为每个事务赋予一个数值型的优先级,优先级最高的事务总是会胜出。

Matt 指出,如果读者对 CockroachDB 的完整事务模型感兴趣,可以查看 GitHub 上的详细设计文档 Tobias Schottdorf 也是 CockroachDB 项目的贡献者。他在回复网友的评论时指出,虽然 CockroachDB 的原子事务策略与使用 MS-DTC 的 SQL Server 分布式事务处理一样都是 2PC,但它们并不相同。XA 有准备和提交两个阶段,提交阶段实际上要做大量的工作。但在 Cockroach 中,“准备”阶段就将值写到了它们最终应该存在的位置,所以“提交”阶段只需要对单个键执行一次写操作,几乎不做什么工作。

另外,在介绍完 CockroachDB 的原子事务策略后,Matt 还特别介绍了 CockroachDB 的清除操作,感兴趣的读者可以进一步阅读


感谢郭蕾对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们,并与我们的编辑和其他读者朋友交流(欢迎加入 InfoQ 读者交流群)。

2015 年 9 月 11 日 19:002946
用户头像

发布了 1008 篇内容, 共 324.5 次阅读, 收获喜欢 296 次。

关注

评论

发布
暂无评论
发现更多内容

【设计模式】组合模式

Andy阿辉

C# 后端 设计模式 8月日更

毕业设计

Chris Cheng

07-数据库存储架构

Lane

前端之数据结构(四)

Augus

数据结构 8月日更

一款非常简单的基于WebRTC的视频预览播放器(具有贴图、视频特效)

liuzhen007

8月日更

每个人都可以说不

escray

学习 极客时间 朱赟的技术管理课 8月日更

趁着课余时间学点Python(六)终止循环,阻断循环

ベ布小禅

8月日更

iOS开发:UICollectionViewCell删除的实现方法

三掌柜

8月日更 8月

毕业设计电商秒杀系统

梦寐凯旋

#架构实战营

架构设计能力提升

arctec

iOS开发:实现点击常用控件弹出地区选择框(万能方法)

三掌柜

8月日更 8月

占楼

IT蜗壳-Tango

8月日更

期末设计

Geek_9cf7b5

Go并发之同步异步、异步回调

Regan Yue

Go 高并发 协程 8月日更

模块四作业

Geek_35a345

【LeetCode】用两个栈实现队列Java题解

HQ数字卡

算法 LeetCode 8月日更

oeasy教您玩转vim - 10 - # 插入新行

o

【Mybatis-Plus 学习笔记】1、MP 简介及入门实例

村雨遥

Java 8月日更 Mybatis-Plus

奥运背后的5G赛场,竟然也这么激烈?

白洞计划

Linux之crontab命令

入门小站

Linux

在线身份证号码提取生日工具

入门小站

工具

电商秒杀系统

Presley

kubernetes/k8s CRI分析-kubelet创建pod分析

良凯尔

Kubernetes 源码分析 Kubernetes Plugin #Kubernetes# cri-o

业务架构图的定位:表达业务层级和关系

arctec

【Flutter 专题】74 图解基本 DropdownButton 下拉选项框按钮

阿策小和尚

Flutter 小菜 0 基础学习 Flutter Android 小菜鸟 8月日更

千万级学生管理系统考试试卷存储方案

徐晓翔

架构实战营 - 模块四作业

Julian Chu

架构实战营

毕业总结

Chris Cheng

架构训练营

Flutter Android 端 Activity/Fragment 流程源码分析

工匠若水

flutter android 0 基础学习 Flutter 8月日更

总结

dwade

#架构实战营

08-消息队列备选架构选择和细化

Lane

聊一下 Mesh 数据平面 Sidecar 与 Service 通信的那些事儿

聊一下 Mesh 数据平面 Sidecar 与 Service 通信的那些事儿

CockroachDB是如何实现分布式原子事务的?-InfoQ