浅淡HBase的ACID

http://archfan.sinaapp.com/2012/04/18/acid-in-hbase/

浅淡HBase的ACID

作者: Neaton 日期: 2012 年 4 月 18 日 发表评论 (0) 查看评论
众所周知,ACID是指原子性(Atomicity),一致性(Consistency),隔离性(Isolation)和持久性(Durability)。

HBase对同一行数据的操作提供ACID保证。HBASE-3584支持多操作事务,HBASE-5229支持多行事务,但原理都是一样的。

那么,HBase中的ACID是如何工作的呢?

HBase采用MVCC来实现ACID,同时HBase中没有混合读写事务。简单说来,HBase的ACID就是在各个RegionServer上维护一个我称之为“严格单调递增事务号”(strictly monotonically increasing transaction numbers)的东东。

当一个写事务(put,delete)开始时将获取到下一个最高事务号,HBase将这个号称为“写入号”(WriteNumber);当一个读事务(scan,get)开始时将获取到上一次提交成功的事务号,这个号被称为“读取点”(ReadPoint)。

每个创建的KeyValue对都会被标记上它的事务写入号。HBase中将这个标签称为”memstore时间戳”。注意将这个时间戳和应用中可见的时间戳区分开。

HBase中一个标准写事务的流程如下:

     锁行,拒绝对相同行的并发写
     获取当前的写入号
     将修改写入“写前日志”WAL(Write Ahead Log)
     将修改写入Memstore,同时用获取到的写入号标记KeyValue对
     提交事务,即尝试将读取点滚到获取到的写入号(这样变更就可以对所有新的Scan可见)
     打开行锁
一个标准读事务的流程如下:

     打开Scanner
     获取当前读取点
     筛选出所有memstore时间戳大于读取点的KeyValue对
     关闭Scanner
尽管实际的流程要更复杂,但上面的流程已经足以阐明要点。另外,你可能注意到,虽然读请求根本没有锁,我们一样实现了ACID。

HBase的事务非常短暂,且所有事务都是串行提交的(我们都知道:所有的事务可以工作都是基于严格串行的提交的,否则一个稍早的未提交的事务可能由于在他之后的事务先提交了而变的可见了)。HBase维护者一个未完成的事务列表。一个事务想提交,必须等到之前所有的事务都提交(注意:变更依然是及时的,并发的,只有提交才是串行的)。

另外,因为HBase不保证RegionServer间的一致性,所以MVCC数据只需要保存在单台RegionServer的内存中。

接着咱们来说说HBase中比较有意思的压缩。HBase中压缩是指:将多个小存储文件(从memstore刷新到磁盘上的)合并成一个大文件,同时进行垃圾回收。这里的垃圾是指过期的,或版本太老,或被标记为删除的KeyValue对。

想象一下,压缩的时候一个Scanner过来取数据,会发生什么呢?

有可能只读出行的一部分。HBase的行由一系列带版本号的KeyValues对组成,这些KeyValue并不能反映事务结束后的最终结果。HBase的解决方案是压缩开始时记录正在读取的最早的一个读取点,然后只压缩比这个读取点小的KeyValue对。这个逻辑让HBase即使在并发刷新memstore到磁盘时也能够保证ACID。HBASE-2856开始支持这个逻辑,HBASE-5569对删除标志支持同样的逻辑。

最后说一句,当KeyValue的memstore时间戳比任何Scanner的读取点都小时,可以被清零。即,对于任何Scanner这条keyValue都是可见的,因为之前的Scanner已经完成了。

补充几点:

     为了不拖延正在等待提交的事务,即使事务失败了,读取点依然会向前滚
     每当变更写入WAL时,就会产生一条记录,并没有单独的提交记录
     一台RegionServer挂了之后,WAL记录写成功的但未执行的事务最终会在另外一台RegionServer上重新执行,WAL记录未写成功的则会被丢弃

猜你喜欢

转载自duanni.iteye.com/blog/1852182