以前的理论是:客户端向ZooKeeper请求 -ROOT- 表的位置,然后通过 -ROOT- 表找 META 表的位置,再通过 META 表定位到Region Server位置。但这个理论已经过时了, -ROOT- 表已经消失在历史的尘埃中。
现在的ZooKeeper直接缓存了 META 表所在的位置信息(本质是一样的,只是这跳过了 -ROOT- 表,这个过程本来就很多余。。),可以直接通过ZooKeeper获取 META 表的位置,后续流程也还是一样的。因为限定了 META 表不可分割,只能存在一个Region,所以存储他的Region Server一定只有一个。下面看看具体怎么定位的。
客户端向服务端发起请求时,会先去定位存储数据的Region位置,代码在HConnectionImplementation#locateRegion方法里。
@Override
public RegionLocations locateRegion(final TableName tableName,
final byte [] row, boolean useCache, boolean retry, int replicaId)
throws IOException {
...
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);
}
}
通过locateRegionInMeta(tableName, row, useCache, retry, replicaId);
方法定位,这个方法里会向hbase:meta表发起请求。既然是HBase的表!!要获取数据就需要用Scan。
byte[] metaKey = HRegionInfo.createRegionName(tableName, row, HConstants.NINES, false);
Scan s = new Scan();
s.setReversed(true);
s.setStartRow(metaKey);
s.setSmall(true);
s.setCaching(1);
Result regionInfoRow = null;
ReversedClientScanner rcs = null;
rcs = new ClientSmallReversedScanner(conf, s, TableName.META_TABLE_NAME, this,
rpcCallerFactory, rpcControllerFactory, getMetaLookupPool(), 0);
regionInfoRow = rcs.next();
Scan的start_row格式是table_name,start_row,stop_row
,例如要找到test表里startRow为row1,stopRow为row9的数据的位置,就会构造一个startRow为test,row1,99999999999999的scan,stopRow是被无视的变量。
发起请求就会再次调用HConnectionImplementation#locateRegion方法,这次定位的是TableName.META_TABLE_NAME,就是hbase:meta,调用的方法是locateMeta(tableName, useCache, replicaId);
,从ZooKeeper中获取到META表的位置信息。
private RegionLocations locateMeta(final TableName tableName,
boolean useCache, int replicaId) throws IOException {
// 缓存判断
...
// 从zookeeper里获取hbase:meta的位置
locations = this.registry.getMetaRegionLocation();
if (locations != null) {
cacheLocation(tableName, locations);
}
}
return locations;
}
然后查询到的结果是这样的。
test,,1509281716605.6a column=info:regioninfo, timestamp=1509282949240, value={ENCODED
369c3c62302852bc15e563 => 6a369c3c62302852bc15e563b28606a9, NAME => 'test,,150928171660
b28606a9. 5.6a369c3c62302852bc15e563b28606a9.', STARTKEY => '', ENDKEY =>
''}
test,,1509281716605.6a column=info:seqnumDuringOpen, timestamp=1509282949240, value=\x0
369c3c62302852bc15e563 0\x00\x00\x00\x00\x00\x00\x09
b28606a9.
test,,1509281716605.6a column=info:server, timestamp=1509282949240, value=192.168.1.108
369c3c62302852bc15e563 :51140
b28606a9.
test,,1509281716605.6a column=info:serverstartcode, timestamp=1509282949240, value=1509
369c3c62302852bc15e563 282943972
b28606a9.
这样就知道这个数据的位置了,Connection就把这个结果缓存住,接着就拿这个定位信息去查询数据了。
在初始情况下,需要请求一次ZooKeeper获得hbase:meta表位置,Scan hbase:meta表获得Region位置,Scan Region获得数据;最差情况下发现缓存的都是错的,都得重来一遍。
-END-