hbase源码系列(三)Client如何找到正确的Region Server
客户端在进行put、delete、get等操作的时候,它都需要数据到底存在哪个Region Server上面,这个定位的操作是通过HConnection.locateRegion方法来完成的。
loc = hConnection.locateRegion(this.tableName, row.getRow());
这里我们首先要讲hbase的两张元数据表-ROOT-和.META.表,它们一个保存着region的分部信息,一个保存着region的详细信息。在《hbase实战》这本书里面详细写了查找过程总共有8步:
(1)客户端首先查询zookeeper -ROOT-表在哪里
(2)zookeeper告诉客户端,-ROOT-在RegionServer RS1上面
(3)客户端向RS1发起查询,哪一个.META表可以查到T1表里面的00009
(4)RS1上的-ROOT-告诉客户端在RS3上面的.META. region M2可以找到
(5)客户端向RS3上的.META. region M2查询T1表的00009行数据在哪个region上,哪一个Region Server可以提供服务
(6)RS3告诉客户端,在RS3上面的region T1R3
(7)客户端向RS3上面的region T1R3发起请求,我要读取00009行
(8)RS3上的region T1R3把数据发给客户端,行,拿去吧
那在代码里面是怎么体验上述过程的呢?好,我们开始查看locateRegion这个方法,打开HConnectionManager这个类。
private HRegionLocation locateRegion(final TableName tableName,
final byte [] row, boolean useCache, boolean retry) {
if (tableName.equals(TableName.META_TABLE_NAME)) {
return this.registry.getMetaRegionLocation();
} else {
// Region not in the cache - have to go to the meta RS
return locateRegionInMeta(TableName.META_TABLE_NAME, tableName, row,
useCache, userRegionLock, retry);
}
}
TableName.META_TABLE_NAME,这个就是我们要找的-ROOT-,在0.96里面它已经被取消了,取而代之的是META表中的第一个regionHRegionInfo.FIRST_META_REGIONINFO,它位置在zk的meta-region-server节点当中的。
好吧,再回到代码里面,我们这里肯定是走else这个路径,我们进入locateRegionInMeta看看。
代码好长啊,我们一点一点看吧,先从缓存里面找,把tableName和rowkey传进去。
if (useCache) {
location = getCachedLocation(tableName, row);
if (location != null) {
return location;
}
}
这里的cache是这样组织的Map<tableName, SoftValueSortedMap<rowkey, HRegionLocation>>, 通过tableName获得它的基于rowkey的子map,这个map是按照key排好序的,如果找不到合适的key,就找比它稍微小一点的key。
接下来就是一个for循环了,默认是尝试31次
HRegionLocation metaLocation = null;
try {
// locate the meta region 还好这个不是玩递归,直接获取meta表所在的位置
metaLocation = locateRegion(parentTable, metaKey, true, false);
if (metaLocation == null) continue; // 通过这方法可以获得Region Server,超值啊
ClientService.BlockingInterface service = getClient(metaLocation.getServerName()); synchronized (regionLockObject) if (useCache) { location = getCachedLocation(tableName, row); if (location != null) { return location; } // 如果表没有被禁用,就预加载缓存 if (parentTable.equals(TableName.META_TABLE_NAME) && (getRegionCachePrefetch(tableName))) { prefetchRegionCache(tableName, row); } // 如果缓存中有,就从缓存中取 location = getCachedLocation(tableName, row); if (location != null) { return location; } }else { // 不需要缓存就在缓存中删掉 forceDeleteCachedLocation(tableName, row); }
从上面的代码分析,它在prefetchRegionCache方法预先缓存了和表和row相关的位置信息,核心的代码如下:
MetaScannerVisitor visitor = new MetaScannerVisitorBase() {
public boolean processRow(Result result) throws IOException { // 把result转换为regionInfo HRegionInfo regionInfo = MetaScanner.getHRegionInfo(result); long seqNum = HRegionInfo.getSeqNumDuringOpen(result);
HRegionLocation loc = new HRegionLocation(regionInfo, serverName, seqNum);
// cache this meta entry
cacheLocation(tableName, null, loc);
return true;
}
}; MetaScanner.metaScan(conf, this, visitor, tableName, row, this.prefetchRegionLimit, TableName.META_TABLE_NAME);
这里面的核心代码只有两行,实现一个MetaScannerVisitor,然后传入到MetaScanner.metaScan扫描一下,metaScan会调用visiter的processRow方法,processRow方法把满足条件的全都缓存起来。下面是条件,有兴趣的人可以看一下,我折叠起来。
HRegionInfo regionInfo = MetaScanner.getHRegionInfo(result);
if (regionInfo == null) {
return true;
}
// possible we got a region of a different table...
if (!regionInfo.getTable().equals(tableName)) {
return false; // stop scanning
}
if (regionInfo.isOffline()) {
// don't cache offline regions
return true;
}
ServerName serverName = HRegionInfo.getServerName(result);
if (serverName == null) {
return true; // don't cache it
}
看一下MetaScanner.metaScan吧,它也是用了new了一个HTable
HTable metaTable = new HTable(TableName.META_TABLE_NAME, connection, null);
然后根据有三种情况,根据情况来构建Scan的StartKey
1.根据rowkey来扫描
2.全表扫
3.根据表的名来
这里讲一下根据rowkey来扫描吧,别的都很简单,它用的是HTable的getRowOrBefore来找到这个Row,只不过因为它是meta表,可以从zk上直接找到位置。
byte[] searchRow = HRegionInfo.createRegionName(tableName, row, HConstants.NINES, false);
Result startRowResult = metaTable.getRowOrBefore(searchRow, HConstants.CATALOG_FAMILY);
HRegionInfo regionInfo = getHRegionInfo(startRowResult);
byte[] rowBefore = regionInfo.getStartKey();
startRow = HRegionInfo.createRegionName(tableName, rowBefore, HConstants.ZEROES, false);
下面就开始Scan了,这个Scan的代码,和我们平常用HTable来扫描表是一样的。
final Scan scan = new Scan(startRow).addFamily(HConstants.CATALOG_FAMILY);
int rows = Math.min(rowLimit, configuration.getInt(HConstants.HBASE_META_SCANNER_CACHING,
HConstants.DEFAULT_HBASE_META_SCANNER_CACHING));
scan.setCaching(rows);
// Run the scan
scanner = metaTable.getScanner(scan);
Result result = null;
int processedRows = 0;
while ((result = scanner.next()) != null) { // 用visitor.processRow来过滤不符合的result if (visitor != null) {
if (!visitor.processRow(result)) break;
}
processedRows++;
if (processedRows >= rowUpperLimit) break;
}
如果没用缓存的情况,就只能走接口的方式了,直接从服务器去了,如果这都找不着,这一次就算结束了。
regionInfoRow = ProtobufUtil.getRowOrBefore(service,metaLocation.getRegionInfo().getRegionName(), metaKey, HConstants.CATALOG_FAMILY); // 下面是具体的实现GetRequest request = RequestConverter.buildGetRowOrBeforeRequest(regionName, row, family);GetResponse response = client.get(null, request);if (!response.hasResult()) return null;return toResult(response.getResult());
好,现在最后总结一下吧:
(1)要查询数据时候,在locateRegion方法要先走locateRegionInMeta这段
(2)从zk当中获取meta表的位置,通过这个位置信息ServerName,获得Region Server的接口,但是这里先不用,留给不用缓存的情况用的
(3)使用缓存的话,如果这个表没被禁用,就先把要定位的整个表的region的位置信息,全部缓存起来
(4)在缓存表的过程当中,我们要借助new HTable(TableName.META_TABLE_NAME, connection, null)来计算startKey和扫描。
(5)把扫描到的表相关的位置信息缓存起来,缓存之后取的过程这里忘了交代了,通过表名找到表对应的一个HRegionInfo,HRegionInfo里面包括startKey和stopKey,用rowkey一比对就知道是哪个region了。
(6)不用缓存的情况,就走接口的方式,构造一个GetRequest,调用Region Server里面的get方法获取到位置信息。
hbase源码系列(三)Client如何找到正确的Region Server的更多相关文章
- 三 Client 如何找到正确的 Region Server
客户端在进行put.delete.get等操作的时候,它都需要数据到底存在哪个Region Server上面,这个定位的操作是通过 Connection.locateRegion方法来完成的. loc ...
- hbase源码系列(二)HTable 探秘
hbase的源码终于搞一个段落了,在接下来的一个月,着重于把看过的源码提炼一下,对一些有意思的主题进行分享一下.继上一篇讲了负载均衡之后,这一篇我们从client开始讲吧,从client到master ...
- hbase源码系列(十二)Get、Scan在服务端是如何处理
hbase源码系列(十二)Get.Scan在服务端是如何处理? 继上一篇讲了Put和Delete之后,这一篇我们讲Get和Scan, 因为我发现这两个操作几乎是一样的过程,就像之前的Put和Del ...
- 9 hbase源码系列(九)StoreFile存储格式
hbase源码系列(九)StoreFile存储格式 从这一章开始要讲Region Server这块的了,但是在讲Region Server这块之前得讲一下StoreFile,否则后面的不好讲下去 ...
- 11 hbase源码系列(十一)Put、Delete在服务端是如何处理
hbase源码系列(十一)Put.Delete在服务端是如何处理? 在讲完之后HFile和HLog之后,今天我想分享是Put在Region Server经历些了什么?相信前面看了<HTab ...
- 10 hbase源码系列(十)HLog与日志恢复
hbase源码系列(十)HLog与日志恢复 HLog概述 hbase在写入数据之前会先写入MemStore,成功了再写入HLog,当MemStore的数据丢失的时候,还可以用HLog的数据来进行恢 ...
- HBase源码系列之HFile
本文讨论0.98版本的hbase里v2版本.其实对于HFile能有一个大体的较深入理解是在我去查看"到底是不是一条记录不能垮block"的时候突然意识到的. 首先说一个对HFile ...
- hbase源码系列(十二)Get、Scan在服务端是如何处理?
继上一篇讲了Put和Delete之后,这一篇我们讲Get和Scan, 因为我发现这两个操作几乎是一样的过程,就像之前的Put和Delete一样,上一篇我本来只打算写Put的,结果发现Delete也可以 ...
- Spring源码系列(三)--spring-aop的基础组件、架构和使用
简介 前面已经讲完 spring-bean( 详见Spring ),这篇博客开始攻克 Spring 的另一个重要模块--spring-aop. spring-aop 可以实现动态代理(底层是使用 JD ...
随机推荐
- Linux中SFTP命令
sftp和ftp是两种协议是不同的,sftp是ssh内含的协议,只要sshd服务器启动了,它就可用,它本身不需要ftp服务器启动. 1.常用登陆方式: 格式:sftp <user>@< ...
- [转] linux(debian)安装USB无线网卡(tp-link TL-WN725N rtl8188eu )
1: 台式机家里面不想再走线了. 于是去某东买了个USB无线网卡.tp的WN725N USB,非常小, 和罗技的优联接收器差不多大. 2: 驱动能自己识别是不指望了,既然是usb网卡,插入USB后 ...
- extjs4学习-01-准备工作
想学习这个,在这里做个笔记. 创建了svn管理,路径http://ip:端口/repos/doc_jnfwz/liuzhenming/extjs4/extjs4 eclipse 中安装插件,支持在js ...
- hbase操作
名称命令表达式 创建表create '表名称','列簇名称1','列簇名称2'....... 添加记录put '表名称', '行名称','列簇名称:','值' 查看记录get '表名称','行名称' ...
- Android开发系列(十五):【Android小游戏成语连连看】第一篇
学了一个多月安卓.由于暑假的时候要给朋友说写个小游戏.并且也想检測下自己的能力,所以说从7号開始就着手写这个小游戏了,前前后后带上课到今天总算是写完了,可是写的这个小游戏还是有非常多问 ...
- Android 7.0 Dialog 无法显示的问题
app 在 Android 7.0 上登录的时候, Dialog 不显示了,但是半透明背景显示 经过搜索和对比,发现出现该问题是因为重写了 getResources() 方法造成的 .重写该方法是为了 ...
- 【Java】日志知识总结和经常使用组合配置(commons-logging,log4j,slf4j,logback)
Log4j Apache的一个开放源码项目,通过使用Log4j,我们能够控制日志信息输送的目的地是控制台.文件.GUI组件.甚至是套接口服务 器.NT的事件记录器.UNIX Syslog守护进程等.用 ...
- 看了一下unity5.6的新功能 以及Timeline
3月31日unity5.6发布,然而timeline(前sequence模块)被delay到unity 2017.上个星期官方又发布了unity 2017的beta版本 抽空看了下 (unity5.6 ...
- Atitit 我们的devops战略与规划 规范 推荐标准
Atitit 我们的devops战略与规划 规范 推荐标准 1. Vm容器化1 2. 热部署tomcat+jrebel 或者resin1 3. 增量更新与差异更新1 4. 补丁提取与应用2 为了方便提 ...
- 智能引导式报错(Class file name must end with .class)
转自:http://blog.sina.com.cn/s/blog_8e761c110101dyj3.html 在使用Eclipse时,有时会出现这样的错误,在使用智能引导式会报错An interna ...