前几篇文章稀稀拉拉的介绍了HBase的MVCC,但也只是停留在概念阶段,这几天学习了MVCC的源码,和大家分享一下。
在看源码之前再简单啰嗦一下MVCC的概念,有助于理解源码的精神。
想像这样一种情景,在数据库进行读操作时,如果有人向其中写入数据,读的人就有可能会看到不一致的数据。当然解决方法很多,最简单的莫过于加锁,让所有读者等待写工作完成再读,但这样会影响并发效率。MVCC很好地解决了这一问题。MVCC是实现HBase数据库高性能的关键技术,主要用于为HBase提供一套一致性数据模型,将HBase的行锁与行的多个版本结合起来,从而既提高了HBase的并发性能,又保证了数据库的一致性。
应用MVCC ,在某个瞬间读者看到的是数据库的一个快照,写者写操作造成的变化在写操作完成之前(或者数据库事务提交之前)对于其他的读者来说是不可见的。当一个 MVCC 数据库需要更一个一条数据记录的时候,它不会直接用新数据覆盖旧数据,而是将旧数据标记为过时并在别处增加新版本的数据。这样就会有存储多个版本的数据,但是只有一个是最新的。这种方式允许读者读取在他读之前已经存在的数据,即使这些在读的过程中半路被别人修改、删除了,也对先前正在读的用户没有影响。这种多版本的方式避免了填充删除操作在内存和磁盘存储结构造成的空洞的开销,但是需要系统周期性整理以真实删除老的、过时的数据。
总结一下,简单说,MVCC就是用同一份数据临时保留多版本的方式,实现并发控制。
概念简单,但实现起来很复杂。真所谓小米想要超越苹果,苹果高管来了一句“easy to say,hard to do”。但机智的雷大大巧妙地在马大大面前引用了马大大的一句名言回应:“梦想还是要有的,万一实现了呢”!
在此还要多说一下,本人看的HBase源码是属于0.98.6版本的,小米的HBase committer为HBase在0.98版中引入了新的写线程模型,为HBase的发展做出了贡献,要点赞!!!
接下来看源码:
MultiVersionConsistencyControl管理Memstore中的读写一致性。是实现MVCC的重要类。
首先看该类变量:
再来看唯一的内部类:
再看初始化方法,MVCC初始化方法的判断逻辑是memstoreRead==memstoreWrite时候才有必要进行初始化:
memstoreReadPoint()方法:
beginMemstoreInsert()方法:
advanceMemstore()方法:
waitForRead()方法:
最后总结下MVCC的读写流程:
写操作:
- 获取行锁后,每个写操作都立即分配一个写序号
- 写操作在保存每个数据cell时都要带上写序号
- 写操作需要申明以这个写序号来完成本次写操作
- 每个读操作开始都分配一个读序号,也称为读取点
- 读取点的值是所有的写操作完成序号中的最大整数(所有的写操作完成序号<=读取点)
- 对某个(row,column)的读取操作r来说,结果是满足写序号为“写序号<=读取点这个范围内”的最大整数的所有cell值的组合