Google为了解决网页索引的增量处理,以及维护数据表和索引表的一致性问题,基于BigTable实现了一个支持分布式事务的存储系统。这里重点讨论这个系统的分布式事务实现,不讨论percolator中为了支持增量计算而实现的Notifications机制。

该系统基于BigTable,支持snapshot isolation隔离级别,这个隔离级别不在ANSI定义的隔离级别范围内。简单来说,就是一个事务看到的是一个stable的数据库的快照。快照隔离相对于可串行化隔离级别的优点是更高的读性能,不需要加锁,MVCC基于BigTable的多版本机制。缺点是有write skew问题,简单来说,对于两个事务T1:b=a+1和T2:a=b+1,初始化a=b=0。串行化的情况下,结果只可能是(a=2,b=1)或者(a=1,b=2),而在快照隔离级别下,结果可能是(a=1,b=1)。这在某些业务场景下是不能接受的。既然有多版本,就需要有版本号,percolator系统使用一个全局递增时间戳服务器来为事务产生时间戳,每个事务开始时拿一个时间戳t1,那么这个事务执行过程中可以读t1之前的数据。提交时再取一下时间戳t2,作为这个事务的提交时间戳。

现在说分布式事务。说起分布式事务第一个想到的就是两阶段提交,这个系统也不例外。客户端作为协调者coordinator,BigTable的tablet server作为参与者participant。 除了实际的表的每个Cell的数据存在BigTable中外,coordinator还将Cell锁信息,事务版本号存在BigTable中。简单来说,如果需要写列C,在BigTable中实际存在三列,分别为C:data,C:lock,C:write。由于BigTable实际上定位一个Value需要三个信息,rowkey,column和timestamp,所以实际上一个 column本身内部可以看成一个timestamp->value的map。那么:

  1. c:write中存事务提交时间戳commit_ts=>start_ts。

  2. c:data这个map中存事务开始时间戳start_ts=>实际列数据

  3. c:lock存start_ts=>(primary cell),primary cell是rowkey和列名的组合,它在两阶段提交容错处理和事务冲突时使用,用来清理由于coordinator失败导致的分布式事务失败留下的锁信息。

举个没有任何冲突例子,假设一个分布式事务T1需要修改两个Cell,C1(Rowkey1:C1)和C2(Rowkey2:C2),C1为primary cell,Value分别为Value1和Value2,并且两个Cell处于不同的tablet server,serverA和serverB。客户端commit之前首先将两个Cell都加入到客户端本地的一个数组中,最后事务commit(包括两阶段的prepare和commit)的时候才将所有Cell发向tablet server。

没有检测到冲突的写事务流程:

prepare阶段:

1. 分布式事务T1启动,从全局时间戳服务器获取事务启动时间戳记作t1_start_ts。

2. 首先写primary cell C1,往C1:data中写入t1_start_ts=>value1,往C1:lock中写入t1_start_ts=>primary cell 表示加锁,同理,写serverB,往C2:data中写入t1_start_ts=>value2, 往C2:lock中写入t1_start_ts=>primary cell

commit阶段:

1. 从全局时间戳服务器获取事务提交时间戳记作t1_commit_ts。

2. 启动一个C1所在的BigTable行事务,包含以下两个操作

    2.1 往primary cell C1:write写入t1_commit_ts=>t1_start_ts(这步是关键)

    2.2 将primary cell的lock release(delete C1:lock,时间戳为t1_start_ts)

3. Commit这个BigTable 事务,这一步实际上将这个事务的数据对外可见,因为随后的一个读事务(事务启动时间戳记作t2_start_ts)读C1之前,会首先读C1:write的小于t2_start_ts的最大的版本的数据获得t1_start_ts,然后拿着t1_start_ts才能去C1:data中读取真正的数据。

4. 将其他secondary cell C2:write中写入t1_commit_ts=>t1_start_ts,release C2的lock

没有检测到冲突的读C1和C2的事务T3流程:

  1. 从全局时间戳服务器获取事务提交时间戳记作t3_start_ts

  2. 分别读C1和C2,读C:write的比t3_start_ts小的最大的一个事务提交时间戳的事务启动时间,然后拿这个事务启动时间去C:data中读真正的数据。

可以看出,一个Cell对外可见是通过写C:write来达到的,t1_commit_ts为事务提交版本号,t1_start_ts为t1这个事务修改后的数据版本号,真正读数据需要拿到t1_start_ts,而读t1_start_ts又需要首先拿到t1_commit_ts。

协调者(Client)宕机容错

假设C1上锁失败,C2上锁成功,那么分布式事务失败会将C2的锁残留在BigTable中。这个残留的锁由后续第一个读或写C2的事务来清除,满足什么样的条件才能清除?满足以下两个条件中的一个即可:1. 写这个lock的客户端在chubby上的结点没了,即客户端死了 2. C2:lock这把锁滞留时间太长了(lock内部保存最后更新时间即可)。  cleanup的操作就是直接delete C2:lock即可,时间戳为t2_start_ts_(percolator论文中此处有笔误)。但是如何知道残留下这个锁的事务是否已经提交?这就需要去读C2的primary cell的write字段,在这个例子里就是读C1:write,记残留下来的锁C2:lock的时间戳为lock_ts(percolator论文这里说的不清楚),那么具体的判断事务是否提交的操作就是读取C1:write的[lock_ts,正无穷)的所有版本,判断是否有一个版本的值是lock_ts,如果有,则说明残留锁的事务已经提交。

事务冲突

Prepare阶段冲突:写C1之前需要首先读取C1:lock,如果有任何一个版本被加上了锁,那么这次分布式事务失败。还有一种冲突是,有其他事务在本事务开始之后commit修改了C1,从而修改了C1:write,这是一种Snapshot isolation需要避免的写写冲突。

Commit阶段冲突:分布式事务提交需要先提交primary cell,再提交其他cell,再提交primary cell时需要先检查自己是否还拿住了primary cell的锁,在这里是C1:lock,即t1_start_ts_版本是否已经被删除。做这个判断的原因是其他事务可以cleanup这个lock,如果它认为这个事务握有锁时间过长或者写入lock的客户端宕机太慢等原因。在这里,primary cell的lock字段是其他事务进行cleanup操作和当前事务提交操作的同步点。

参考资料:

http://static.googleusercontent.com/media/research.google.com/en/us/pubs/archive/36726.pdf

https://github.com/XiaoMi/themis/

分布式事务实现-Percolator的更多相关文章

  1. Google关于Spanner的论文中分布式事务的实现

    Google关于Spanner的论文中分布式事务的实现 Google在Spanner相关的论文中详细的解释了Percolator分布式事务的实现方式, 而且用简洁的伪代码示例怎么实现分布式事务; Pe ...

  2. MVCC/分布式事务简介

    之前我们学习了RocksDB,但这还只是一个最基础的存储引擎.如果想把它在生产环境中用起来,还需要解决很多问题: 如何从单机扩展到分布式? 如何实现事务,并对事务进行并发控制? 用户接口能不能高级一点 ...

  3. 数据库分布式事务XA规范介绍及Mysql底层实现机制

    1. 引言 分布式事务主要应用领域主要体现在数据库领域.微服务应用领域.微服务应用领域一般是柔性事务,不完全满足ACID特性,特别是I隔离性,比如说saga不满足隔离性,主要是通过根据分支事务执行成功 ...

  4. 群集中的MS DTC分布式事务协调器

    MS DTC在大多数SQL 服务器下都需要安装,若只是安装数据库引擎或Analysis 服务可不安装DTC.如果后需要使用分布式事务,则可在SQL Server群集安装完成后再安装DTC. 一.群集M ...

  5. 事务使用中如何避免误用分布式事务(System.Transactions.TransactionScope)

    1:本地事务DbTransaction和分布式事务TransactionScope的区别: 1.1:System.Data.Common.DbTransaction: 本地事务:这个没什么好说了,就是 ...

  6. 没有活动事务 链接服务器的 OLE DB 访问接口 "SQLNCLI" 无法启动分布式事务

    在windows2003下执行分布式事务的时候出现如下情况. 一. 问题现象在执行分布式事务时,在sql server 2005下收到如下错误: 链接服务器"xxxxxxx"的 O ...

  7. 已禁用对分布式事务管理器(MSDTC)的网络访问的解决方法之一

    C# ASP.NET项目提示上述错误,在代码中使用分布式事务提示添加或修改到数据库的时候.添加数据到数据库时,不会设置实体类的主键字段.

  8. 【转】PostgreSQL分布式事务配置

    XA是open group提出的分布式事务处理规范,JTA支持XA规范,JTA只规定了接口,有些应用容器提供实现,也有一些三方的开源实现可用,比如Atomikos. 如果PostgreSQL参与分布式 ...

  9. 分布式事务(一)两阶段提交及JTA

    原创文章,同步发自作者个人博客 http://www.jasongj.com/big_data/two_phase_commit/ 分布式事务 分布式事务简介 分布式事务是指会涉及到操作多个数据库(或 ...

随机推荐

  1. PPT-常用快捷键

    Ctrl+Shift+C   复制文本格式 Ctrl+Shift+V   粘贴文本格式 Ctrl+B 应用粗体格式Ctrl+U 应用下划线Ctrl+l 应用斜体格式 全选  Ctrl + A 光标之后 ...

  2. mysql pdo设置显示报错

    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);    

  3. Maven3路程(一)环境搭建

    好长时间不用Java,今天看了下,Maven集成成主流了,在技术水平与日俱进的同时,感叹下IT行业必须有活到老学到老的精神. 先说下环境: Maven:Maven 3.0.5 解压后路径:F:\Mav ...

  4. redis学习(四)redis事务

    redis事务 1.redis事务介绍 redis的事务可以理解为一系列串行命令的集合.redis的事务和单条命令一样,都是redis的最小执行单位,因此一个事务内的命令,要么全部执行,要么全部不执行 ...

  5. 利用docker hub做中转拉取google的k8s镜像

    1.背景 部署kubernetes,需要FQ.但是在初始化的时候,即是FQ了有的镜像pull依然会超时,导致初始化失败.而你又不想使用国内的一些镜像源,因为更新不及时.很多新功能和插件都不会包括,只想 ...

  6. Java 集合并交补

    示例 package com.example; import java.util.ArrayList; import java.util.Arrays; import java.util.Collec ...

  7. Netbeans 8.0配置Python开发环境

    1. 菜单栏:工具->插件->设置->添加 配置如下信息: http://deadlock.netbeans.org/hudson/job/nbms-and-javadoc/last ...

  8. Java SDK夯住(Hang)问题排查

    夯住(Hang)是指程序仍在运行,卡在某个方法调用上,没有返回也没有异常抛出:卡住时间从几秒到几小时不等. Java程序发生Hang时,应该首先使用 jstack 把java进程的堆栈信息保存下来 , ...

  9. webpack3新特性简介

    6月20号webpack推出了3.0版本,官方也发布了公告.根据公告介绍,webpack团队将未来版本的改动聚焦在社区提出的功能需求,同时将保持一个快速.稳定的发布节奏.本文主要依据公告内容,简单介绍 ...

  10. Redis有序集合操作

    有序集合存储着成员和分值之间的映射,并且提供了分值处理命令,以及根据分值大小有序的获取或扫描成员和分值的命令 (常用命令) ZADD : ZADD key-name score member [sco ...