分享

[HBase]Region location


问题导读

1.如何某一个row对应的region是在哪台Region server上?
2.  -ROOT-、.META.表作用是什么?







HBase的table是该region切分的,client操作一个row的时候,如何知道这个row对应的region是在哪台Region server上呢?这里有个region location过程。主要涉及到2张系统表,-ROOT-,.META.。其结构见图

1.jpg


在zookeeper的/hbase/root-region-server节点中存着-ROOT-表所在的Region Server地址。
-ROOT-表的一个row代表着META的一个region信息,其key的结构是META表名,META表Region的startkey,RegionId。其value的主要保存regioninfo和server信息。ROOT表不能split
.META.表的一个row代表着用户表的一个region信息,其key的结构是其实就是用户表的regionName,用户表名,startKey,RegionId。其value同样保存着regioninfo和server信息。META表可以split,但是一个region默认有128M,可以存上亿个用户表的region信息,所以一般不会split。
其查找过程如下:
1.通过zk getData拿-ROOT-表的location
2.RPC -ROOT-表的rs,getClosestRowBefore,拿row对应的meta表的region location
3.RPC .META.表的某一个region,拿该row在真实table所在的region location
4.RPC对应region

region location需要3次网络IO,为了提升性能,client会cache数据。
LocationCache是一个2级Map,第一级的key是tableName的hash值,第二级的key是starRow,用SoftValueSortedMap包装了TreeMap实现,用软引用实现cache,内存不够时才会回收。Cache里存在META表和用户表的region location信息。
其代码实现如下,0.94版本:
HConnectionManager locateRegion入口

  1. private HRegionLocation locateRegion(final byte [] tableName,
  2.       final byte [] row, boolean useCache)
  3.     throws IOException {
  4.         .......
  5.         //检查下都应的zkTracker是否启动
  6.       ensureZookeeperTrackers();
  7.         //如果是-ROOT-表,则通过zk节点/hbase/root-region-server获取-ROOT-表所在的Location
  8.       if (Bytes.equals(tableName, HConstants.ROOT_TABLE_NAME)) {
  9.         try {
  10.                 //通过zk的getData接口拿节点数据,此处会等待节点数据就位或者超时
  11.           ServerName servername = this.rootRegionTracker.waitRootRegionLocation(this.rpcTimeout);
  12.           LOG.debug("Looked up root region location, connection=" + this +
  13.             "; serverName=" + ((servername == null)? "": servername.toString()));
  14.           if (servername == null) return null;
  15.         //返回一个拼装的HRegionLocation,因为-ROOT-表只有一个region,而且不会split
  16.           return new HRegionLocation(HRegionInfo.ROOT_REGIONINFO,
  17.             servername.getHostname(), servername.getPort());
  18.         } catch (InterruptedException e) {
  19.           Thread.currentThread().interrupt();
  20.           return null;
  21.         }
  22.       }
  23.         //如果是.META.表,则请求.META.表,这里的row其实就是请求row拼装的regionName,类似test,key1,99999999999999
  24.         //如果没命中cache,则继续请求-ROOT-表,拿到这个row对应的.META.表的region location
  25.       else if (Bytes.equals(tableName, HConstants.META_TABLE_NAME)) {
  26.         return locateRegionInMeta(HConstants.ROOT_TABLE_NAME, tableName, row,
  27.             useCache, metaRegionLock);
  28.       }
  29.         //如果是用户表,则请求用户表,这里的row就是key1
  30.         //如果没命中cache,则请求.META.表,获取该row对应的region location
  31.       else {
  32.         // Region not in the cache - have to go to the meta RS
  33.         return locateRegionInMeta(HConstants.META_TABLE_NAME, tableName, row,
  34.             useCache, userRegionLock);
  35.       }
  36.     }
复制代码

locateRegionInMeta方法
  1. private HRegionLocation locateRegionInMeta(final byte [] parentTable,
  2.       final byte [] tableName, final byte [] row, boolean useCache,
  3.       Object regionLockObject)
  4.     throws IOException {
  5.       HRegionLocation location;
  6.       // If we are supposed to be using the cache, look in the cache to see if
  7.       // we already have the region.
  8.         //先读cache,cache没有再往上找
  9.         //注意如果rowkey的region locatin变化了,RPC的时候会失败,客户端做重试的时候useCache是false
  10.       if (useCache) {
  11.         location = getCachedLocation(tableName, row);
  12.         if (location != null) {
  13.           return location;
  14.         }
  15.       }
  16.       // build the key of the meta region we should be looking for.
  17.       // the extra 9's on the end are necessary to allow "exact" matches
  18.       // without knowing the precise region names.
  19.         //先拼一个想查找的key,类似于test,key1,99999999999999
  20.       byte [] metaKey = HRegionInfo.createRegionName(tableName, row,
  21.         HConstants.NINES, false);
  22.         //默认重试10次
  23.       for (int tries = 0; true; tries++) {
  24.         //找不到
  25.         if (tries >= numRetries) {
  26.           throw new NoServerForRegionException("Unable to find region for "
  27.             + Bytes.toStringBinary(row) + " after " + numRetries + " tries.");
  28.         }
  29.         
  30.         HRegionLocation metaLocation = null;
  31.         try {
  32.           // locate the root or meta region
  33.                 //递归查找parentTable
  34.           metaLocation = locateRegion(parentTable, metaKey);
  35.           // If null still, go around again.
  36.           if (metaLocation == null) continue;
  37.                 //找到对应Region server地址之后,可以发起RPC请求了。
  38.                 //这里先生成一个RPC Proxy对象,具体RPC分析见后文
  39.           HRegionInterface server =
  40.             getHRegionConnection(metaLocation.getHostname(), metaLocation.getPort());
  41.           Result regionInfoRow = null;
  42.           // This block guards against two threads trying to load the meta
  43.           // region at the same time. The first will load the meta region and
  44.           // the second will use the value that the first one found.
  45.           synchronized (regionLockObject) {
  46.             // If the parent table is META, we may want to pre-fetch some
  47.             // region info into the global region cache for this table.
  48.                 //如果parentTable是.META.表,则预先获取.META.的一些数据,默认10条
  49.             if (Bytes.equals(parentTable, HConstants.META_TABLE_NAME) &&
  50.                 (getRegionCachePrefetch(tableName)) )  {
  51.               prefetchRegionCache(tableName, row);
  52.             }
  53.             // Check the cache again for a hit in case some other thread made the
  54.             // same query while we were waiting on the lock. If not supposed to
  55.             // be using the cache, delete any existing cached location so it won't
  56.             // interfere.
  57.             if (useCache) {
  58.               location = getCachedLocation(tableName, row);
  59.               if (location != null) {
  60.                 return location;
  61.               }
  62.             }
  63.                 //如果不使用cache,则清除之,比如row对应的region发生了分裂,用老的location启动rpc时会抛异常,此时通过useCache=fasle重新
  64.                 //寻址,并把老的cache删掉
  65.             else {
  66.               deleteCachedLocation(tableName, row);
  67.             }
  68.             // Query the root or meta region for the location of the meta region
  69.                 //发起RPC请求,获取<=该key的行
  70.             regionInfoRow = server.getClosestRowBefore(
  71.             metaLocation.getRegionInfo().getRegionName(), metaKey,
  72.             HConstants.CATALOG_FAMILY);
  73.           }
  74.           if (regionInfoRow == null) {
  75.             throw new TableNotFoundException(Bytes.toString(tableName));
  76.           }
  77.                 //region信息,做校验,region会处于不稳定状态
  78.           byte [] value = regionInfoRow.getValue(HConstants.CATALOG_FAMILY,
  79.               HConstants.REGIONINFO_QUALIFIER);
  80.           if (value == null || value.length == 0) {
  81.             throw new IOException("HRegionInfo was null or empty in " +
  82.               Bytes.toString(parentTable) + ", row=" + regionInfoRow);
  83.           }
  84.           // convert the row result into the HRegionLocation we need!
  85.                 //反序列化
  86.           HRegionInfo regionInfo = (HRegionInfo) Writables.getWritable(
  87.               value, new HRegionInfo());
  88.           // possible we got a region of a different table...
  89.                 //一些校验
  90.           if (!Bytes.equals(regionInfo.getTableName(), tableName)) {
  91.             throw new TableNotFoundException(
  92.                   "Table '" + Bytes.toString(tableName) + "' was not found, got: " +
  93.                   Bytes.toString(regionInfo.getTableName()) + ".");
  94.           }
  95.           if (regionInfo.isSplit()) {
  96.             throw new RegionOfflineException("the only available region for" +
  97.               " the required row is a split parent," +
  98.               " the daughters should be online soon: " +
  99.               regionInfo.getRegionNameAsString());
  100.           }
  101.           if (regionInfo.isOffline()) {
  102.             throw new RegionOfflineException("the region is offline, could" +
  103.               " be caused by a disable table call: " +
  104.               regionInfo.getRegionNameAsString());
  105.           }
  106.                 //该region的server location
  107.           value = regionInfoRow.getValue(HConstants.CATALOG_FAMILY,
  108.               HConstants.SERVER_QUALIFIER);
  109.           String hostAndPort = "";
  110.           if (value != null) {
  111.             hostAndPort = Bytes.toString(value);
  112.           }
  113.           ......
  114.           // Instantiate the location
  115.           String hostname = Addressing.parseHostname(hostAndPort);
  116.           int port = Addressing.parsePort(hostAndPort);
  117.           location = new HRegionLocation(regionInfo, hostname, port);
  118.                 //cache之
  119.           cacheLocation(tableName, location);
  120.           return location;
  121.         } catch (TableNotFoundException e) {
  122.           // if we got this error, probably means the table just plain doesn't
  123.           // exist. rethrow the error immediately. this should always be coming
  124.           // from the HTable constructor.
  125.           throw e;
  126.         } catch (IOException e) {
  127.           if (e instanceof RemoteException) {
  128.             e = RemoteExceptionHandler.decodeRemoteException((RemoteException) e);
  129.           }
  130.           if (tries < numRetries - 1) {
  131.             .......
  132.           } else {
  133.             throw e;
  134.           }
  135.           // Only relocate the parent region if necessary
  136.                 //网络有问题,则重新找
  137.           if(!(e instanceof RegionOfflineException ||
  138.               e instanceof NoServerForRegionException)) {
  139.             relocateRegion(parentTable, metaKey);
  140.           }
  141.         }
  142.                 //重试次数越多,sleep越长,interrupt则退出重试
  143.         try{
  144.           Thread.sleep(ConnectionUtils.getPauseTime(this.pause, tries));
  145.         } catch (InterruptedException e) {
  146.           Thread.currentThread().interrupt();
  147.           throw new IOException("Giving up trying to location region in " +
  148.             "meta: thread is interrupted.");
  149.         }
  150.       }
  151.     }
复制代码












欢迎加入about云群425860289432264021 ,云计算爱好者群,关注about云腾讯认证空间

已有(4)人评论

跳转到指定楼层
pengsuyun 发表于 2015-1-2 08:38:26
你的文章写得很好,高版本的hbase中 /hbase/root-region-server 中的root-region-server的znode已经不存在了,我的版本是0.98.9,取代的是meta-region-server.
回复

使用道具 举报

Joker 发表于 2015-1-4 21:39:49
这里HBase ROOT表startkey指的是.META.,也就是表名吗?
1就的RegionID吗??

望赐教
回复

使用道具 举报

Joker 发表于 2015-1-4 21:53:10
还是指tableN是startkey
回复

使用道具 举报

spftoto 发表于 2018-6-7 16:28:23
前面文字好像看懂了 哈哈 代码看起来有点困难,要加强java的学习了
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

推荐上一条 /2 下一条