HBase之MVCC
HBase在保证写数据一致性的同时,还保证读取的高性能。这一机制的实现就是通过MultiVersionConsistencyControl来控制的,简称MVCC
HBase为什么需要并发控制?
我们知道HBase是可以通过客户端往服务器写数据的,如果存在多个客户端,可能同时在操作某一个Region的某一行的某一列。
我们首先假定一个并发写的场景:
假设两个客户端同时往表中同一个Row的同一个Family(general)的两个列(qualifier)name和 price写数据,对于MemStore来讲这些数据是并发写入的。
写数据流程如下:
写入MemStore:讲每一个cell单元格写入MemStore缓存起来
写入HLog
上面例子中其实有4个key-value也就是4个cell,每一个请求来的时候,MemStore会写两个,但是这四个key-value写入顺序我们是无法在并发环境下保证的,就可能会出现以下情况:
本来在写完general:name= Gibson Guitar的时候,然后接着写入122.89
但是这个时候另一个线程抢在它之前执行了写入349.99
怎么办呢?
# 我们可以先获取Row Lock
# 更新MemStore,写入cell
# 再将cell写入HLog
# 释放Row Lock
那现在写的问题解决了,那我们读取数据的时候会不会有问题呢?
我们试想一下:当第一组数据general:name和 general:price<Gibson Guitar,122.89>已经写入完毕,已经释放锁
第二组数据开始写,正在写或准备写price349.99的时候,读请求过来,他已经把general:name=>GibsonGuitar更细了,但是还没有更新general:price=>349.99,这时候读取的数据就是
很显然这是有问题的。
怎么办呢?我们对读请求加锁,毫无疑问这肯定是没有问题的
但是不管读还是写都会涉及到锁的竞争,降低了系统的吞吐量。
所以MVCC出场了。
原理如下:
无论读写,每一次都被给一个Number,写操作叫做WriteNumber,读操作叫做memstoreRead。
一般情况下,为了确保数据一致性,在对数据进行写操作的时候,需要等到前面的操作完成,不然可能造成数据丢失。
所以当一个写操作来的时候,会把这个请求写入链表末尾writeQueue
,完成的操作标记为完成,并且从链表头部删除。
而最大的writeNumber会记录在memstoreRead,从而告知其他的所有的等待者,现在操作的最新sequenceId是多少,保证每一个等待着看到的数据都是最新的