Hbase事务

摘要:
为了实现事务特性,HBase采用了各种并发控制策略,包括各种锁机制、MVCC机制等。在HBase 0.98之前,要确保WAL写入的原子性并不容易,这取决于WAL的结构。需要注意的是,版本必须递增,版本递增的范围在一定程度上决定了事务是什么。例如,如果HBase是区域级增量版本,则事务是区域级事务。

原文:http://hbasefly.com/2017/07/26/transaction-2/

1、关于hbase事务

HBase目前只支持行级事务;
可以保证行级数据的原子性、一致性、隔离性以及持久性,即通常所说的ACID特性。
为了实现事务特性,HBase采用了各种并发控制策略,包括各种锁机制、MVCC机制等。


2、hbase事务原子性

hbase写数据:HBase数据会首先写入WAL,再写入Memstore。

写入Memstore异常很容易可以回滚,因此保证写入/更新原子性只需要保证写入WAL的原子性即可。HBase 0.98之前版本需要
保证WAL写入的原子性并不容易,这由WAL的结构决定。假设一个行级事务更新R行中的3列(c1, c2, c3),来看看之前版本
和当前版本的WAL结构:

1. 之前版本WAL结构:
<logseq1-for-edit1>:<KeyValue-for-edit-c1>

<logseq2-for-edit2>:<KeyValue-for-edit-c2>

<logseq3-for-edit3>:<KeyValue-for-edit-c3>

每个KV都会形成一个WAL单元,这样一行事务更新多少列就会产生多少个WAL单元。在将这些WAL单元append到日志文件的时候,一旦出现宕机或其他异常,
就会出现部分写入成功的情况,原子性更新就无法保证。

2. 当前版本WAL结构:
<logseq#-for-entire-txn>:<WALEdit-for-entire-txn>

<logseq#-for-entire-txn>:<-1, 3, <Keyvalue-for-edit-c1>, <KeyValue-for-edit-c2>, <KeyValue-for-edit-c3>>

通过这种结构,每个事务只会产生一个WAL单元。这样就可以保证WAL写入时候的原子性。


3、hbase事务强一致性保证

* 写写并发控制
* 批量写入多行的写写并发
* 读写并发控制


为什么需要写写并发控制 :

现在假设有两个并发写入请求同时进来,分别对同一行数据进行写入。下图所示RowKey为Greg,现在分别更新列族info下的Company列和Role列:

image


如果没有任何并发控制策略的话,写入数据(先写WAL,再写memstore)可能会出现不同KV写入”交叉”现象,如下图所示:

image


这样的话,用户最终读取到的数据就会产生不一致,如下:

image


如何实现写写并发控制:

实现写写并发其实很简单,只需要在写入(或更新)之前先获取行锁,如果获取不到,说明已经有其他线程拿了该锁,就需要
不断重试等待或者自旋等待,直至其他线程释放该锁。拿到锁之后开始写入数据,写入完成之后释放行锁即可。这种行锁机制
是实现写写并发控制最常用的手段,后面可以看到MySQL也是使用行锁来实现写写并发的。


如何实现批量写入多行的写写并发:

HBase支持批量写入(或批量更新),即一个线程同时更新同一个Region中的多行记录。那如何保证当前事务中的批量写入与
其他事务中的批量写入的并发控制呢?思路还是一样的,使用行锁。但这里需要注意的是必须使用两阶段锁协议,即:

(1) 获取所有待写入(更新)行记录的行锁

(2) 开始执行写入(更新)操作

(3) 写入完成之后再统一释放所有行记录的行锁

不能更新一行锁定(释放)一行,多个事务之间容易形成死锁。两阶段锁协议就是为了避免死锁,MySQL事务写写并发控制同
样使用两阶段锁协议。


4、读写并发控制

为什么需要读写并发控制:

现在我们通过在写入更新之前加锁、写入更新之后释放锁实现写写并发控制,那读写之间是不是也需要一定的并发控制呢?

如果不加并发控制,会出现什么现象呢?接着看下图:

image


上图分别是两个事务更新同一行数据,现在假设第一个事务已经更新完成,在第二个事务更新到一半的时候进来一个读请求,

如果没有任何并发控制的话,读请求就会读到不一致的数据,Company列为Restaurant,Role列为Engineer,如下图所示:

image

可见,读写之间也需要一种并发控制来保证读取的数据总能够保持一致性,不会出现各种诡异的不一致现象。


如何实现读写并发控制:

实现读写并发最简单的方法就是仿照写写并发控制 – 加锁。但几乎所有数据库都不会这么做,性能太差,对于读多写少的应用
来说必然不可接受。那还有其他方法吗?

当然,这就是今天要重点提到的MVCC机制 – Mutil Version Concurrent Control。HBase中MVCC机制实现主要分为两步:

(1) 为每一个写(更新)事务分配一个Region级别自增的序列号

(2) 为每一个读请求分配一个已完成的最大写事务序列号

示意图如下所示:

image

上图中两个写事务分别分配了序列号1和序列号2,读请求进来的时候事务1已经完成,事务2还未完成,因此分配事务1对应的序列号1给读请求。

此时序列号1对本次读可见,序列号2对本次读不可见,读到的数据是:

image


具体实现中,所有的事务都会生成一个Region级别的自增序列,并添加到队列中,如下图最左侧队列,其中最底端为已经提交
的事务,队列中的事务为未提交事务。现假设当前事务编号为15,并且写入完成(中间队列红色框框),但之前的写入事务还
未完成(序列号为12、13、14的事务还未完成),此时当前事务必须等待,而且对读并不可见,直至之前所有事务完成之后才
会对读可见(即读请求才能读取到该事务写入的数据)。如最右侧图,15号事务之前的所有事务都成功完成,此时Read Point
就会移动到15号事务处,表示15号事务之前的所有改动都可见。

image


可能有朋友有疑问:如果这两个自增序列是同一个序列,那是不是这个队列的顺序必须与事务写入WAL的顺序一致?
如果不一致有什么问题?如果要求一致的话怎么才能实现?

千万不要查看1.1.2的代码,会把你彻底搞混的!建议阅读更高版本的相关代码!

所以,MVCC的精髓是写入的时候分配递增版本信息(SequenceId),读取的时候分配唯一的版本用于读取可见,比之大的版
本不可见。这里需要注意版本必须递增,而且版本递增的范围一定程度上决定了事务是什么事务,比如HBase是Region级别的
递增版本,那么事务就是region级别事务。MySQL中版本是单机递增版本,那么MySQL事务就支持单机跨行事务。Percolator
中版本是集群递增版本,那么Percolator事务就是分布式事务。


5、hbase事务持久性保证

HBase事务持久化可以理解为WAL持久化,
目前实现了多种持久化策略:SKIP_WAL,ASYNC_WAL,SYNC_WAL,FSYNC_WAL。
SKIP_WAL表示不写WAL,这样写入更新性能最好,但在RegionServer宕机的时候有可能会丢失部分数据;
ASYNC_WAL表示异步将WAL持久化到硬盘,因为是异步操作所以在异常的情况下也有可能丢失少量数据;
SYNC_WAL表示同步将WAL持久化到操作系统缓存,再由操作系统将数据异步持久化到磁盘,这种场景下RS宕掉并不会丢失
数据,当操作系统宕掉会导致部分数据丢失;
FSYNC_WAL表示WAL写入之后立马落盘,性能相对最差。目前实现中FSYNC_WAL并没有实现!用户可以根据业务对数据丢
失的敏感性在客户端配置相应的持久化策略。






免责声明:文章转载自《Hbase事务》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Java中的异常处理机制的简单原理和应用?mysql 分类表设计下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

HBase性能优化方法总结

4.1 HBase性能优化方法总结(一):表的设计 4.1.1 Pre-Creating Regions 默认情况下,在创建HBase表的时候会自动创建一个region分区,当导入数据的时候,所有的HBase客户端都向这一个region写数据,直到这个region足够大了才进行切分。一种可以加快批量写入速度的方法是通过预先创建一些空的regions,这样...

[翻译]——MySQL Server Variable: sync_binlog (Doc ID 1501926.1)

  本文对MySQL Server Variable: sync_binlog (Doc ID 1501926.1)这篇文章进行了翻译,如有翻译不当或错误的地方敬请指正。     译文地址:https://www.cnblogs.com/kerrycode/p/14167941.html   In this Document Purpose Scope...

sqlserver存储过程

1、  存储过程 1)  尽量将代码段放到TRY…CATCH…。 但凡使用C#写过代码的人,都知道C#中TRY…CATCH…的运行和出错跳转逻辑,而SQL Server2005中,其运行和出错跳转逻辑与在C#中是一致的。TRY…CATCH…是SQL Server2005中新增的,功能强大,且很好用。 2) 定义变量时,可以使用前缀的方式标识变量的类型。在S...

SQL serve 数据库--视图、事物、分离附加、备份还原

视图是数据库中的一种虚拟表,与真实的表一样,视图包含一系列带有名称的行和列数据。行和列数据用来自定义视图的查询所引用的表,并且在引用视图时动态生成。 视图只能用来查询,不能增删改;不允许出现重复列 1. 视图的概念 视图是从一个或者多个表中导出的,其行为与表相似,但视图是一个虚拟表。视图可以在已经存在的视图的基础上定义。在视图上用户可以使用SELECT语...

大数据量下高并发同步的讲解

对于我们开发的网站,如果网站的访问量非常大的话,那么我们就需要考虑相关的并发访问问题了。而并发问题是绝大部分的程序员头疼的问题, 但话又说回来了,既然逃避不掉,那我们就坦然面对吧~今天就让我们一起来研究一下常见的并发和同步吧。 为了更好的理解并发和同步,我们需要先明白两个重要的概念:同步和异步 1、同步和异步的区别和联系 所谓同步,可以理解为在执行完一个函...

SQLServer数据库(二)

数据库设计:就是将数据库中的数据库实体及这些数据库实体之间的关系,进行规划和结构化的过程。 项目开发过程: 需求分析 概要设计 详细设计 代码编写 运行测试 打包发行 数据库的系统分析基本步骤:收集信息、标识实体、标识每个实体需要存储的详细信息、标识实体之间的关系。 实体,就是指现实世界中具有区分其它事物的特征或属性,并与其他实体有联系的实体。实体一般是名...