HBase MVCC 代码阅读(一)
MultiVersionConcurrencyControl.java,版本 0.94.1
MultiVersionConsistencyControl 管理 memstore 中的读写一致性。该类实现了一种机制,达到如下的目的:
- 提供接口让 reader 知道可以忽略哪些元素项
- 提供一个新的 WriteNumber 给 writer
- 将写更新提供给 reader(通过原子事务)
1 变量
主要包含两个变量:
- memstoreRead 前面文章中提到的 ReadPoint
- memstoreWrite 前面文章中提到的 WriteNumber
变量描述private volatile long
,私有,长整形,volatile 标记,线程间可见。
readWriters 私有 object 常量,加锁时候用。
writeQueue 写操作队列,默认构造函数初始化的LinkedList,元素类型为 WriterEntry。注意,LinkedList 这个双向链表数据结构,适合随机增、删,但不是线程安全的。
perThreadReadPoint 线程局部变量,保存当前线程获取的 readPoint,static final
,ThreadLocal,用 Long.MAX_VALUE初始化。
FIXED_SIZE 一个 MVCC 对象占用内存空间的大小,包括:一个锁对象 readWriters,两个 long 型变量 memstoreRead 和 memstoreWrite,两个应用对象的地址:writeQueue 和 perThreadReadPoint。
2 内嵌类
WriteEntry 每个 writer 持有一个,保存 writeNumber,并用一个 completed 标记是否写完。
实际上就是对理论中 writeNumber 的包装:通过 writeNumber 对应到一个writer,通过 completed 标记 writer 是否写完。
3 方法
3.1 构造方法
public MultiVersionConsistencyControl();
唯一的一个构造方法,无参数,将 memstoreRead 和 memstoreWrite 初始化为0。
两个变量的值一般不会为0,后续会用初始化方法,将两个变量的值设置为一个正常值。
3.2 初始化方法
public void initialize(long startPoint);
初始化 memstoreRead 和 memstoreWrite 。
在初始化的时候,会用 synchronized 对 writeQueue 加锁。
判断 memstoreWrite 是否和 memstoreRead 相同,如果两者不同,认为这个是已经用过的 MVCC 对象,不需要再做初始化;如果相同,则将 memstoreRead 和 memstoreWriet 设置为输入参数。
initialize 方法唯一一处引用在 org.apache.hadoop.hbase.regionserver.HRegion.initializeRegionInternals
方法中。
HRegion 包含一个私有的常量 mvcc ,对象声明的时候用 MVCC 的缺省构造方法初始化。HRegion 的 org.apache.hadoop.hbase.regionserver.HRegion.initialize
对 HRegion 初始化的时候,调用了 initialiazeRegionInterals
,初始化 Region 的内部对象,把 mvcc 初始化为一个合理的值。
所以,上面 MVCC 初始化方法的判断逻辑是 memstoreRead == memstoreWrite
时候才有必要进行初始化。
3.3 memstoreRead相关
在实现的时候,ReadPoint 包含两类,一个是全局的,由 memstoreRead 记录的,这个是所有线程均可见、可改的,一个是局部的, ThreadLocal 的 readPoint,每个线程有一个自己的副本,且这个不保证与全局的 readPoint 一样。在代码实现的时候,需要对这两类 readPoint 小心处理。
3.3.1 memstoreReadPoint
public long memstoreReadPoint();
返回当前 MVCC 记录的 memstoreRead。在两处被用到:
一处是 MVCC 的 resetThreadReadPoint,用 mvcc 的全局 readpoint 更新线程局部的 readPoint。
一处是在 HRegion 类中,getSmallestReadPoint 方法,比较该 Region 上所有 scanner 持有的 readPoint,以及 Region MVCC 对象的 readPoint,在这所有中找到一个最小的。
3.3.2 ThreadReadPoint
ThreadReadPoint 相关4个方法,均标记为 static 。
public static long getThreadReadPoint();
获取线程局部 readPoint,主要由 memstore scanner 调用,以确认跳过哪些值。
分别在 org.apahce.hadoop.hbase.regionserver.MemStore.MemStoreScanner.getNext
方法和 org.apahce.hadoop.hbase.regionserver.StoreFileScanner.skipKVsNewerThanReadpoint
方法中被调用。
在 getNext
方法中,获取线程当前的 readPoint,scanner 在扫描的时候,所有比 readPonit 老的值才可以被读到。
在 skipKVsNewerThanReadpoint
方法中则想法,获取线程当前的 readPoint 后,把所有比 readPoint 新的 KV 对象会被跳过。
public static void setThreadReadPoint(long readPoint);
设置线程局部 readPoint。
主要在两个地方用到:
org.apahce.hadoop.hbase.regionserver.HRegion.RegionScannerImpl
配合对象自己的 readPt 使用。
构造方法,根据隔离级别参数,如果为 READ_UNCOMMITED,将线程局部 readPoint 设置为 long.MAX_VALUE。
next()
方法, 带 synchronized 标记,在开始取数据之前,先用 readPt 更新线程局部 readPoint,然后再取值。
reseek()
方法,带 synchronized 标记,重新定位之前,用 readPt 更新线程局部的 readPoint 标记。
org.apahce.hadoop.hbase.regionserver.Store
compactStore
方法。如前面理论描述中提到的,先获取当前 region 所有读者中持有的最老的 readPoint,用 setThreadReadPoint
方法更新线程局部 readPoint。后续做 compact,以这个smallestReadPoint 为基准。
由上可见,线程局部维护的 ThreadReadPoint 不自动更新,可以随时被读取,但是在所有写操作之前,需要对线程局部的 ThreadReadPoint 更新,写入一个确定的值,以确认最新。
public static void resetThreadReadPoint();
将线程局部 readPoint 重置为0。Find Usages 工具发现该方法实际未被使用。
public static long resetThreadReadPoint(MultiVersionConsistencyControl mvcc);
用一个 MVCC 对象的 memstoreReadPoint 更新当前线程的局部 readPoint,并返回更新后的局部 readPoint。
3.4 memstoreWrite相关
涉及4个方法,入口是 beginMemstoreInsert()
和 completeMemstoreInsert()
,completeMemstoreInsert()
在实现的时候,先调用 advanceMemstore()
后调用 waitForRead()
,以下依次说明。
public WriteEntry beginMemstoreInsert();
获取一个最新的 writerNumber,核心就是干这一件事。
对 writeQueue 加锁,获取一个新的 memstoreWrite,利用新的 memstore 新建个 WriteEntry 对象,并添加到 writeQueue 链表中,最后将新建的 WriteEntry 对象返回。
该方法的调用都在 HRegion 类中:
applyFamilyMapToMemstore 传入参数包含 WriteEntry 对象,如果该对象为 null,则调用
beginMemstoreInsert()
会创建一个 WriterEntry 且已加入 writeQueue。doMiniBatchMutation 批量修改,put 或者 delete。通过
beginMemstoreInsert()
获取一个包含最新的 WriteNumber 的 WriteEntryinternalFlushcache flush 的实现方法,获取最新的 writeNumber
mutateRowsWithLocks region 内的原子修改,获取最新的 writeNumber
public void completeMemstoreInsert(WriteEntry e);
按序调用后面两个方法,没有其他。
boolean advanceMemstore(WriteEntry e);
"从头开始遍历writeQueue,移除所有已完成的WriteEntry对象,最后将memstoreRead更新为最新已完成的memstoreWrite;"
boolean advanceMemstore(WriteEntry e) {
synchronized (writeQueue) {
e.markCompleted();
long nextReadValue = -1;
boolean ranOnce=false;
while (!writeQueue.isEmpty()) {
ranOnce=true;
WriteEntry queueFirst = writeQueue.getFirst();
if (nextReadValue > 0) {
if (nextReadValue+1 != queueFirst.getWriteNumber()) {
throw new RuntimeException("invariant in completeMemstoreInsert violated, prev: "
+ nextReadValue + " next: " + queueFirst.getWriteNumber());
}
}
if (queueFirst.isCompleted()) {
nextReadValue = queueFirst.getWriteNumber();
writeQueue.removeFirst();
} else {
break;
}
}
if (!ranOnce) {
throw new RuntimeException("never was a first");
}
if (nextReadValue > 0) {
synchronized (readWaiters) {
memstoreRead = nextReadValue;
readWaiters.notifyAll();
}
}
if (memstoreRead >= e.getWriteNumber()) {
return true;
}
return false;
}
}
public void waitForRead(WriteEntry e);
等待全局的 readPoint 更新到当前 writer 的事务号。"阻塞当前线程,直到memstoreRead等于当前WriteEntry的memstoreWrite,至此表明当前WriteEntry之前的所有更新事务都已经完成"
正如之前 解释 HBase MVCC 实现原理的时候提到的,所有事务号小于 readPoint 的事务,被认为是"确定安全"了,相关的线程也就可以被释放了。
public void waitForRead(WriteEntry e) {
boolean interrupted = false;
synchronized (readWaiters) {
while (memstoreRead < e.getWriteNumber()) {
try {
readWaiters.wait(0);
} catch (InterruptedException ie) {
// We were interrupted... finish the loop -- i.e. cleanup --and then
// on our way out, reset the interrupt flag.
interrupted = true;
}
}
}
if (interrupted) Thread.currentThread().interrupt();
}
4 最新版本
MVCC 最新版本的代码参见:MultiVersionConcurrencyControl.java
从变量到方法都有了明显的变化,下一篇专门比较下。
参考
HBase MVCC 代码阅读(一)的更多相关文章
- 代码阅读分析工具Understand 2.0试用
Understand 2.0是一款源代码阅读分析软件,功能强大.试用过一段时间后,感觉相当不错,确实可以大大提高代码阅读效率.由于Understand功能十分强大,本文不可能详尽地介绍它的所有功能,所 ...
- Android 上的代码阅读器 CoderBrowserHD 修改支持 go 语言代码
我在Android上的代码阅读器用的是 https://github.com/zerob13/CoderBrowserHD 改造的版本,改造后的版本我放在 https://github.com/ghj ...
- Linux协议栈代码阅读笔记(二)网络接口的配置
Linux协议栈代码阅读笔记(二)网络接口的配置 (基于linux-2.6.11) (一)用户态通过C库函数ioctl进行网络接口的配置 例如,知名的ifconfig程序,就是通过C库函数sys_io ...
- [置顶] Linux协议栈代码阅读笔记(一)
Linux协议栈代码阅读笔记(一) (基于linux-2.6.21.7) (一)用户态通过诸如下面的C库函数访问协议栈服务 int socket(int domain, int type, int p ...
- 图形化代码阅读工具——Scitools Understand
Scitools出品的Understand 2.0.用了很多年了,比Source Insight强大很多.以前的名字叫Understand for C/C++,Understand for Java, ...
- Python - 关于代码阅读的一些建议
初始能力 让阅读思路保持清晰连贯,主力关注在流程架构和逻辑实现上,不被语法.技巧和业务流程等频繁地阻碍和打断. 建议基本满足以下条件,再开始进行代码阅读: 具备一定的语言基础:熟悉基础语法,常用的函数 ...
- MediaInfo代码阅读
MediaInfo是一个用来分析媒体文件的开源工具. 支持的文件非常全面,基本上支持所有的媒体文件. 最近是在做HEVC开发,所以比较关注MediaInfo中关于HEVC的分析与处理. 从Meid ...
- Tools - 一些代码阅读的方法
1 初始能力 让阅读思路清晰连贯,保持在程序的流程架构和逻辑实现上,不被语法.编程技巧和业务流程等频繁地阻碍和打断. 语言基础:熟悉基础语法,常用的函数.库.编程技巧等: 了解设计模式.构建工具.代码 ...
- Bleve代码阅读(二)——Index Mapping
引言 Bleve是Golang实现的一个全文检索库,类似Lucene之于Java.在这里通过阅读其代码,来学习如何使用及定制检索功能.也是为了通过阅读代码,学习在具体环境下Golang的一些使用方式. ...
随机推荐
- python派QQ邮件
#encoding=utf-8 __author__ = 'ds' #文件名冲突 from email.mime.text import MIMEText import smtplib if __na ...
- sql小总结
---------------------------------------------------------------------------------------------------- ...
- Linux下的变化的主机名步骤
Linux下变化的主机名gpdb 步骤1.执行vi /etc/sysconfig/network命令 NETWORKING=yesHOSTNAME=gpdb 第二步.执行hostname gpdb令 ...
- 模拟linux下的ls -l命令
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h&g ...
- freemarker 空白处理
1 一个简短的引论 HTML 和 XML 都不是对空白敏感的,可是这么多多余的空白是非常令人头疼的,并且添加处理后的 HTML 文件大小也是不是必需的.当然,对于空白敏感的方式的输出这依然是个大问题. ...
- LevelDB初体验
近期工作须要找一个能使用磁盘存储数据,对写要求比較苛刻,须要每秒达100000TPS,读的时候须要能10000TPS左右,不能占用太多内存.单节点满足这个要求的常见有Redis.Memcached等, ...
- java maven quartz exampe 实用指南
pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w ...
- 网络资源(6) - EJB视频
2014_08_24 http://v.youku.com/v_show/id_XMjE0NjE3MDA0.html?f=5227828 01_EJB3.0_下载安装与运行jboss -------- ...
- 【Android界面实现】可旋转的汽车3D模型效果的实现
转载请注明出处:http://blog.csdn.net/zhaokaiqiang1992 今天要给大家介绍的是怎样实现可旋转的汽车3D模型. 先看实现效果 这仅仅是静态图,实际上,这个模型是能够依据 ...
- Controller和View的交互
Controller和View的交互 目录 ASP.NET MVC搭建项目后台UI框架—1.后台主框架 ASP.NET MVC搭建项目后台UI框架—2.菜单特效 ASP.NET MVC搭建项目后台UI ...