客户端在进行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 客户端设置缓存优化查询。

其实查询无非是一个 HBase 的 RPC 计算公式 、然后给API 提供值。

RPCs = (Rows * Cols per Row) / Min(Cols per Row, Batch Size)/ Scanner Caching 。

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就和分页的值一样就行。

当caching和batch都为1的时候,我们要返回10行具有20列的记录,就要进行201次RPC,因为每一列都作为一个单独的Result来返回,这样是我们不可以接受的。


三 Client 如何找到正确的 Region Server的更多相关文章

  1. hbase源码系列(三)Client如何找到正确的Region Server

    客户端在进行put.delete.get等操作的时候,它都需要数据到底存在哪个Region Server上面,这个定位的操作是通过HConnection.locateRegion方法来完成的. loc ...

  2. client 如何找到正确的RegionServer(HBase -ROOT-和.META.表)

    在HBase中,大部分的操作都是在RegionServer完成的,Client端想要插入,删除,查询数据都需要先找到相应的RegionServer.什么叫相应的RegionServer?就是管理你要操 ...

  3. 未找到或无法访问服务器 请验证实例名称是否正确并且SQL Server 已配置为允许远程连接

    无法连接到sql server 2008服务器 报下错误 其他信息    在与SQL Server建立连接时出现与网络相关的或特定于实例的错误 未找到或无法访问服务器请验证实例名称是否正确并且SQL ...

  4. Hbase Region Server整体架构

    Region Server的整体架构 本文主要介绍Region的整体架构,后续再慢慢介绍region的各部分具体实现和源码 RegionServer逻辑架构图 RegionServer职责 1.    ...

  5. 第三篇——第二部分——第一文 SQL Server镜像简介

    原文:第三篇--第二部分--第一文 SQL Server镜像简介 原文出处:http://blog.csdn.net/dba_huangzj/article/details/26951563 镜像是什 ...

  6. 请验证实例名称是否正确并且 SQL Server 已配置为允许远程连接。 (provider: Named Pipes Provider, error: 40 - 无法打开到 SQL Server 的连接)

    程序异常,错误信息:在与 SQL Server 建立连接时出现与网络相关的或特定于实例的错误.未找到或无法访问服务器.请验证实例名称是否正确并且 SQL Server 已配置为允许远程连接. (pro ...

  7. JeeSite如何正确连接SQL SERVER 数据库

    JeeSite如何正确连接SQL SERVER 数据库 jeesite介绍 感谢jeesite项目的作者thinkgem. 没有你我也不会更改这数据源非了恁大的劲,,,,嘻嘻嘻说多了. JeeSite ...

  8. 【SpringCloud Eureka源码】从Eureka Client发起注册请求到Eureka Server处理的整个服务注册过程(下)

    目录 一.Spring Cloud Eureka Server自动配置及初始化 @EnableEurekaServer EurekaServerAutoConfiguration - 注册服务自动配置 ...

  9. Slack完整教学与上手心得:找到正确的团队沟通之道

    Slack完整教学与上手心得:找到正确的团队沟通之道 时间 2015-06-13 09:21:42 逐鹿网 原文  http://www.zhulu.com/article/5519.html 主题 ...

随机推荐

  1. HCF4094(CD4094)应用

    管脚说明和内部逻辑图 注:管脚图为HCF4094,内部逻辑图为CD4094(HCF4094内部逻辑图在datasheet不清晰,且复杂). 其中控制管脚有3个:STROBE-DATA-CLOCK,Ou ...

  2. swift语言点评十四-继承

    Overriding A subclass can provide its own custom implementation of an instance method, type method, ...

  3. 3ds Max制作碗实例教程

    一. 碗的建模.模型的结果如图WB—1所示: 图WB—1 1. 创建圆柱,并调节参数,转换到多边形,最终的结果图WB—2所示: 图WB—2 2.使用Inset(插入)插入一个面,再次执行Extrude ...

  4. 【codeforces 255D】Mr. Bender and Square

    [题目链接]:http://codeforces.com/problemset/problem/255/D [题意] 给你一个n*n的方框; 给你一个方块;(以下说的方块都是单位方块) 每一秒钟,可以 ...

  5. 我的Android进阶之旅------&gt;怎样将Activity变为半透明的对话框?

    我的Android进阶之旅------>怎样将Activity变为半透明的对话框?能够从两个方面来考虑:对话框和半透明. 在定义Activity时指定Theme.Dialog主题就能够将Acti ...

  6. hdoj--1005--Number Sequence(规律题)

    Number Sequence Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) ...

  7. 2.安装 CLI和CLI的工作原理

    转自:https://www.gruntjs.net/getting-started 还在使用 Grunt 0.3 版本吗?请查看 Grunt 0.3 注意事项 在继续学习前,你需要先将Grunt命令 ...

  8. deque 归纳

    deque是STL里面的常见容器,它的本质是一个队列,但是与队列不同是的是,它可以两边进出. 下面是STL的一些常见操作. que.assign(beg,end) 将[beg; end)区间中的数据赋 ...

  9. POJ 3662 二分+Dijkstra

    题意: 思路: 二分+Disjktra 二分一个值 如果某条边的边权比它小,则连上边权为0的边,否则连上边权为1的边 最后的d[n]就是最小要免费连接多少电话线. //By SiriusRen #in ...

  10. 美国人教你这样用Google,你真的会变特工

    转自微博:黑客师: 用了这么久的谷歌,今天才发现. 第一篇 在搜索框上输入:“indexof/”inurl:lib 再按搜索你将进入许多图书馆,并且一定能下载自己喜欢的书籍. 在搜索框上输入:“ind ...