irpas技术客

Hbase源码分析(六)Region定位(中)2021SC@SDUSC_珍珠没有奶茶^_^_hbase源码分析

未知 7520

文章目录 前言介绍总结


前言

此篇文章将继续叙述Region的定位过程


介绍

在分析locateRegion()方法前,我们先折回去看看使用缓存情况的处理,它也是调用的locateRegion()方法,只不过传入的使用缓存标志位useCache为true这一个区别而已,好了,殊途同归,这里我们就只研究locateRegion()方法就行了,代码如下:

@Override public RegionLocations locateRegion(final TableName tableName, final byte[] row, boolean useCache, boolean retry) throws IOException { return locateRegion(tableName, row, useCache, retry, RegionReplicaUtil.DEFAULT_REPLICA_ID); } @Override public RegionLocations locateRegion(final TableName tableName, final byte[] row, boolean useCache, boolean retry, int replicaId) throws IOException { checkClosed(); if (tableName == null || tableName.getName().length == 0) { throw new IllegalArgumentException("table name cannot be null or zero length"); } if (tableName.equals(TableName.META_TABLE_NAME)) { return locateMeta(tableName, useCache, replicaId); } else { // Region not in the cache - have to go to the meta RS return locateRegionInMeta(tableName, row, useCache, retry, replicaId); } }

locateRegion()方法上来先做一些必要的检查: 1、判断连接是否已关闭的标志位closed,为true则直接抛出IOException异常;

2、判断表名tableName,表名为空的话直接抛出IllegalArgumentException异常。 然后,根据表是否为meta表,做以下处理: 1、如果是meta表,直接调用locateMeta()方法进行定位; 2、如果不是meta表,cache中没有,需要访问meta RS,调用locateRegionInMeta()方法进行定位; 我们今天先看非meta表,进入locateRegionInMeta()方法,代码如下:

private RegionLocations locateRegionInMeta(TableName tableName, byte[] row, boolean useCache, boolean retry, int replicaId) throws IOException { // If we are supposed to be using the cache, look in the cache to see if we already have the // region. if (useCache) { RegionLocations locations = getCachedLocation(tableName, row); if (locations != null && locations.getRegionLocation(replicaId) != null) { return locations; } } // build the key of the meta region we should be looking for. // the extra 9’s on the end are necessary to allow “exact” matches // without knowing the precise region names. byte[] metaStartKey = RegionInfo.createRegionName(tableName, row, HConstants.NINES, false); byte[] metaStopKey = RegionInfo.createRegionName(tableName, HConstants.EMPTY_START_ROW, “”, false); Scan s = new Scan().withStartRow(metaStartKey).withStopRow(metaStopKey, true) .addFamily(HConstants.CATALOG_FAMILY).setReversed(true).setCaching(5) .setReadType(ReadType.PREAD);

switch (this.metaReplicaMode) { case LOAD_BALANCE: int metaReplicaId = this.metaReplicaSelector.select(tableName, row, RegionLocateType.CURRENT); if (metaReplicaId != RegionInfo.DEFAULT_REPLICA_ID) { // If the selector gives a non-primary meta replica region, then go with it. // Otherwise, just go to primary in non-hedgedRead mode. s.setConsistency(Consistency.TIMELINE); s.setReplicaId(metaReplicaId); } break; case HEDGED_READ: s.setConsistency(Consistency.TIMELINE); break; default: // do nothing } int maxAttempts = (retry ? numTries : 1); boolean relocateMeta = false; for (int tries = 0; ; tries++) { if (tries >= maxAttempts) { throw new NoServerForRegionException("Unable to find region for " + Bytes.toStringBinary(row) + " in " + tableName + " after " + tries + " tries."); } if (useCache) { RegionLocations locations = getCachedLocation(tableName, row); if (locations != null && locations.getRegionLocation(replicaId) != null) { return locations; } } else { // If we are not supposed to be using the cache, delete any existing cached location // so it won't interfere. // We are only supposed to clean the cache for the specific replicaId metaCache.clearCache(tableName, row, replicaId); } // Query the meta region long pauseBase = this.pause; takeUserRegionLock(); try { // We don't need to check if useCache is enabled or not. Even if useCache is false // we already cleared the cache for this row before acquiring userRegion lock so if this // row is present in cache that means some other thread has populated it while we were // waiting to acquire user region lock. RegionLocations locations = getCachedLocation(tableName, row); if (locations != null && locations.getRegionLocation(replicaId) != null) { return locations; } if (relocateMeta) { relocateRegion(TableName.META_TABLE_NAME, HConstants.EMPTY_START_ROW, RegionInfo.DEFAULT_REPLICA_ID); } s.resetMvccReadPoint(); try (ReversedClientScanner rcs = new ReversedClientScanner(conf, s, TableName.META_TABLE_NAME, this, rpcCallerFactory, rpcControllerFactory, getMetaLookupPool(), metaReplicaCallTimeoutScanInMicroSecond)) { boolean tableNotFound = true; for (;;) { Result regionInfoRow = rcs.next(); if (regionInfoRow == null) { if (tableNotFound) { throw new TableNotFoundException(tableName); } else { throw new IOException( "Unable to find region for " + Bytes.toStringBinary(row) + " in " + tableName); } } tableNotFound = false; // convert the row result into the HRegionLocation we need! locations = MetaTableAccessor.getRegionLocations(regionInfoRow); if (locations == null || locations.getRegionLocation(replicaId) == null) { throw new IOException("RegionInfo null in " + tableName + ", row=" + regionInfoRow); } RegionInfo regionInfo = locations.getRegionLocation(replicaId).getRegion(); if (regionInfo == null) { throw new IOException("RegionInfo null or empty in " + TableName.META_TABLE_NAME + ", row=" + regionInfoRow); } // See HBASE-20182. It is possible that we locate to a split parent even after the // children are online, so here we need to skip this region and go to the next one. if (regionInfo.isSplitParent()) { continue; } if (regionInfo.isOffline()) { throw new RegionOfflineException("Region offline; disable table call? " + regionInfo.getRegionNameAsString()); } // It is possible that the split children have not been online yet and we have skipped // the parent in the above condition, so we may have already reached a region which does // not contains us. if (!regionInfo.containsRow(row)) { throw new IOException( "Unable to find region for " + Bytes.toStringBinary(row) + " in " + tableName); } ServerName serverName = locations.getRegionLocation(replicaId).getServerName(); if (serverName == null) { throw new NoServerForRegionException("No server address listed in " + TableName.META_TABLE_NAME + " for region " + regionInfo.getRegionNameAsString() + " containing row " + Bytes.toStringBinary(row)); } if (isDeadServer(serverName)) { throw new RegionServerStoppedException( "hbase:meta says the region " + regionInfo.getRegionNameAsString() + " is managed by the server " + serverName + ", but it is dead."); } // Instantiate the location cacheLocation(tableName, locations); return locations; } } } catch (TableNotFoundException e) { // if we got this error, probably means the table just plain doesn't // exist. rethrow the error immediately. this should always be coming // from the HTable constructor. throw e; } catch (LocalConnectionClosedException cce) { // LocalConnectionClosedException is specialized instance of DoNotRetryIOE. // Thrown when we check if this connection is closed. If it is, don't retry. throw cce; } catch (IOException e) { ExceptionUtil.rethrowIfInterrupt(e); if (e instanceof RemoteException) { e = ((RemoteException)e).unwrapRemoteException(); } if (e instanceof CallQueueTooBigException) { // Give a special check on CallQueueTooBigException, see #HBASE-17114 pauseBase = this.pauseForCQTBE; } if (tries < maxAttempts - 1) { LOG.debug("locateRegionInMeta parentTable='{}', attempt={} of {} failed; retrying " + "after sleep of {}", TableName.META_TABLE_NAME, tries, maxAttempts, maxAttempts, e); } else { throw e; } // Only relocate the parent region if necessary relocateMeta = !(e instanceof RegionOfflineException || e instanceof NoServerForRegionException); } finally { userRegionLock.unlock(); } try{ Thread.sleep(ConnectionUtils.getPauseTime(pauseBase, tries)); } catch (InterruptedException e) { throw new InterruptedIOException("Giving up trying to location region in " + "meta: thread is interrupted."); } } }

locateRegionInMeta()方法是对非Meta表中特定行row所在Region位置信息的检索,它本质上是通过检索HBase中Meta表数据来获取对应非Meta表中行row对应的Region位置信息的,其处理逻辑如下: 1、根据标志位useCache确定:如果我们支持在缓存中查找,先在缓存中看看是否我们已经有该Region,调用的是getCachedLocation()方法,传入tableName和row即可,存在即返回,否则继续;

2、缓存中没有,构造一个scan,先根据表名tableName、行row、字符串"99999999999999",调用HRegionInfo的createRegionName()方法,创建一个Region Name:metaKey;

3、构造一个Scan,scan的起始行为上述metaKey,并且是一个反向小scan,即reversed small Scan;

4、确定重试上限次数localNumRetries:如果标志位retry为true的话,重试上限次数localNumRetries取numTries,即取参数hbase.client.retries.number,参数未配置的话默认为31;

5、在一个循环内,当重试次数tries未达到上限localNumRetries且未定位到对应Region位置信息时:

5.1、先判断重试次数tries是否达到上限localNumRetries,达到的话,直接抛出NoServerForRegionException异常; .2、根据是否支持从缓存中取来判断:

5.2.1、如果支持使用缓存的话,每次再从缓存中取一遍,存在即返回,否则继续;

5.2.2、如果我们不支持使用缓存,删除任何存在的相关缓存,以确保它不会干扰我们的查询,调用metaCache的clearCache()方法,根据tableName和row来删除;

5.3、构造ClientSmallReversedScanner实例rcs,从meta表中查找,而meta表的表名固定为hbase:meta,它的namespace为"meta",qualifier为"meta",获取scanner,注意,这一步实际上是一个内嵌的scan,它也需要根据表和行进行Region的定位,而这个表就是HBase中的Meta表,既然从Meta表中查找数据,那么就又折回到上面针对Meta表和非Meta标的的if…else…判断了,关于Meta表的定位我们稍等再讲;

5.4、通过scanner的next()方法,获取唯一的结果regionInfoRow;

5.5、关闭ClientSmallReversedScanner;

5.6、如果regionInfoRow为空,直接抛出TableNotFoundException异常;

5.7、将Result转换为我们需要的RegionLocations,即regionInfoRow->locations;

5.8、从locations中获取Region信息HRegionInfo;

5.9、做一些必要的数据和状态校验,比如:

5.9.1、验证表名是否一致;

5.9.2、验证Rgion是否已分裂;

5.9.3、验证Rgion是否已下线;

5.9.4、从locations中根据replicaId获取ServerName,验证ServerName是否已死亡;

5.10、调用cacheLocation()方法缓存获得的位置信息locations,并返回;

5.11、如果中间出现异常,则当前线程休眠一段时间,再次重试,休眠的时间与pause和tries有关,越往后,停顿时间一般越长(波动时间除外)。

总结

以上就是今天要讲的内容,本文介绍了将需要读写的行Row准确的定位到其所在Region和RegionServer上,HBase是如何实现数据的检索的。


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #hbase源码分析 #PUBLIC #RegionLocations #tableName