博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
hbase源码系列(三)Client如何找到正确的Region Server
阅读量:6815 次
发布时间:2019-06-26

本文共 6524 字,大约阅读时间需要 21 分钟。

  客户端在进行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            }
View Code

  看一下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方法获取到位置信息。

  

转载于:https://www.cnblogs.com/cenyuhai/p/3695565.html

你可能感兴趣的文章
F5集群配置公共irule,解决X-Frame-Options漏洞及host头漏洞
查看>>
mysql 创建日期列之timestamp
查看>>
VMM系列之使用VMM服务器构建 Hyper-V主机(4)
查看>>
详测 Generics Collections TList (7): Items、Contains
查看>>
配置FTP服务器(2) 本地用户下载和上传
查看>>
多线程编程(11) - 多线程同步之 Mutex (互斥对象)[续]
查看>>
【Java每日一题】20161214
查看>>
requireJs 模块化简陋版本
查看>>
我的友情链接
查看>>
How to upgrade vim to version 8 on CentOS 7
查看>>
xcode pod 报import 找不到 pods的支持问题解决方法之一
查看>>
nginx配置让任何文件在浏览器中显示文本text/plain
查看>>
思科路由器×××配置-- 动态 site-to-site ×××(上)
查看>>
Visual Studio统计有效代码行数
查看>>
Qt连接Oracle数据库常见问题
查看>>
45个实用的JavaScript技巧、窍门和最佳实践
查看>>
sqlserver 2005 列字符串拼接
查看>>
TSharding源码阅读-MapperShardingInitializer
查看>>
XWifiMouse早期写的一个Android鼠标App
查看>>
postgres预写式日志的内核实现详解-wal记录写入
查看>>