三 Client 如何找到正确的 Region Server
客户端在进行put、delete、get等操作的时候,它都需要数据到底存在哪个Region Server上面,这个定位的操作是通过
Connection.locateRegion方法来完成的。
loc = hConnection.locateRegion(this.tableName, row.getRow());
这里我们首先要讲hbase的两张元数据 表-ROOT-和.META. 表,它们一个保存着 region的分部信息,一个保存着region的详细信息。在《hbase实战》这本书里面详细写了查找过程总共有8步:
(1)客户端-》zk -(ROOT-表在哪)
(2)zk->客户端(-ROOT-在RegionServer X node中)
(3)客户端->x RegionServer 发起查询,-> 去那个META表可以查到T1表里的 YYYY rowkey
(4)x RegionServer -ROOT-> 客户端 (RegionServer在 X1 上的.META. region 的 M2可以找到
(5)客户端-》RegionServer X1 上的.META. region M2查询T1表的YYYY key行数据在哪个region上,哪一个Region Server可以提供服务
(6)RegionServer X1 告诉客户端,在RegionServer X1 上面的region RG33
(7)客户端向RegionServer X1上面的region RG33 发起请求,我要读取00009行
(8)RegionServer X1 的 region RG33 把数据发给客户端;
代码里面查看 HConnectionManager.locateRegion() 的实现过程

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 regionsreturn 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当中获取 root - 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 的 RPC 计算公式 、然后给API 提供值。
Hadoop的MR运算中,Hbase可以作为输入数据源参与运算,其中作为HTable的迭代器Scan有几个使用技巧
涉及的方法如下:
public void setBatch(int batch)
public void setCaching(int caching)
public void setCacheBlocks(boolean cacheBlocks)
public void setBatch(int batch) :
为设置获取记录的列个数,默认无限制,也就是返回所有的列
public void setCaching(int caching):
每次从服务器端读取的行数,默认为配置文件中设置的值
public void setCacheBlocks(boolean cacheBlocks):
为是否缓存块,默认缓存,我们分内存,缓存和磁盘,三种方式,一般数据的读取为内存->缓存->磁盘,当MR的时候为非热点数据,因此不需要缓存
因此在MR的时候最好设置如下:
scan.setCacheBlocks(false);
scan.setCaching(200);//大了占内存,但是rpc少
scan.setBatch(6);//你需要的列
在查询的时候,按照查询的列数动态设置batch,如果全查,则根据自己所有的表的大小设置一个折中的数值,caching就和分页的值一样就行。
三 Client 如何找到正确的 Region Server的更多相关文章
- hbase源码系列(三)Client如何找到正确的Region Server
客户端在进行put.delete.get等操作的时候,它都需要数据到底存在哪个Region Server上面,这个定位的操作是通过HConnection.locateRegion方法来完成的. loc ...
- client 如何找到正确的RegionServer(HBase -ROOT-和.META.表)
在HBase中,大部分的操作都是在RegionServer完成的,Client端想要插入,删除,查询数据都需要先找到相应的RegionServer.什么叫相应的RegionServer?就是管理你要操 ...
- 未找到或无法访问服务器 请验证实例名称是否正确并且SQL Server 已配置为允许远程连接
无法连接到sql server 2008服务器 报下错误 其他信息 在与SQL Server建立连接时出现与网络相关的或特定于实例的错误 未找到或无法访问服务器请验证实例名称是否正确并且SQL ...
- Hbase Region Server整体架构
Region Server的整体架构 本文主要介绍Region的整体架构,后续再慢慢介绍region的各部分具体实现和源码 RegionServer逻辑架构图 RegionServer职责 1. ...
- 第三篇——第二部分——第一文 SQL Server镜像简介
原文:第三篇--第二部分--第一文 SQL Server镜像简介 原文出处:http://blog.csdn.net/dba_huangzj/article/details/26951563 镜像是什 ...
- 请验证实例名称是否正确并且 SQL Server 已配置为允许远程连接。 (provider: Named Pipes Provider, error: 40 - 无法打开到 SQL Server 的连接)
程序异常,错误信息:在与 SQL Server 建立连接时出现与网络相关的或特定于实例的错误.未找到或无法访问服务器.请验证实例名称是否正确并且 SQL Server 已配置为允许远程连接. (pro ...
- JeeSite如何正确连接SQL SERVER 数据库
JeeSite如何正确连接SQL SERVER 数据库 jeesite介绍 感谢jeesite项目的作者thinkgem. 没有你我也不会更改这数据源非了恁大的劲,,,,嘻嘻嘻说多了. JeeSite ...
- 【SpringCloud Eureka源码】从Eureka Client发起注册请求到Eureka Server处理的整个服务注册过程(下)
目录 一.Spring Cloud Eureka Server自动配置及初始化 @EnableEurekaServer EurekaServerAutoConfiguration - 注册服务自动配置 ...
- Slack完整教学与上手心得:找到正确的团队沟通之道
Slack完整教学与上手心得:找到正确的团队沟通之道 时间 2015-06-13 09:21:42 逐鹿网 原文 http://www.zhulu.com/article/5519.html 主题 ...
随机推荐
- HCF4094(CD4094)应用
管脚说明和内部逻辑图 注:管脚图为HCF4094,内部逻辑图为CD4094(HCF4094内部逻辑图在datasheet不清晰,且复杂). 其中控制管脚有3个:STROBE-DATA-CLOCK,Ou ...
- swift语言点评十四-继承
Overriding A subclass can provide its own custom implementation of an instance method, type method, ...
- 3ds Max制作碗实例教程
一. 碗的建模.模型的结果如图WB—1所示: 图WB—1 1. 创建圆柱,并调节参数,转换到多边形,最终的结果图WB—2所示: 图WB—2 2.使用Inset(插入)插入一个面,再次执行Extrude ...
- 【codeforces 255D】Mr. Bender and Square
[题目链接]:http://codeforces.com/problemset/problem/255/D [题意] 给你一个n*n的方框; 给你一个方块;(以下说的方块都是单位方块) 每一秒钟,可以 ...
- 我的Android进阶之旅------>怎样将Activity变为半透明的对话框?
我的Android进阶之旅------>怎样将Activity变为半透明的对话框?能够从两个方面来考虑:对话框和半透明. 在定义Activity时指定Theme.Dialog主题就能够将Acti ...
- hdoj--1005--Number Sequence(规律题)
Number Sequence Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) ...
- 2.安装 CLI和CLI的工作原理
转自:https://www.gruntjs.net/getting-started 还在使用 Grunt 0.3 版本吗?请查看 Grunt 0.3 注意事项 在继续学习前,你需要先将Grunt命令 ...
- deque 归纳
deque是STL里面的常见容器,它的本质是一个队列,但是与队列不同是的是,它可以两边进出. 下面是STL的一些常见操作. que.assign(beg,end) 将[beg; end)区间中的数据赋 ...
- POJ 3662 二分+Dijkstra
题意: 思路: 二分+Disjktra 二分一个值 如果某条边的边权比它小,则连上边权为0的边,否则连上边权为1的边 最后的d[n]就是最小要免费连接多少电话线. //By SiriusRen #in ...
- 美国人教你这样用Google,你真的会变特工
转自微博:黑客师: 用了这么久的谷歌,今天才发现. 第一篇 在搜索框上输入:“indexof/”inurl:lib 再按搜索你将进入许多图书馆,并且一定能下载自己喜欢的书籍. 在搜索框上输入:“ind ...