phoenix预创建表的源码分析
下面主要分析了普通物理表,索引表和salt表创建时的预分区情况
在 ConnectionQueryServicesImpl.createTableInternal 方法当中,要分析表是否需要预先分区,
if (parent != null && tableType == PTableType.INDEX && indexType == IndexType.LOCAL) {
//如果是本地索表则拿到拿到物理数据表的所有region所在的regionserver,
tableProps.put(MetaDataUtil.PARENT_TABLE_KEY, parent.getPhysicalName().getString());
tableProps.put(MetaDataUtil.IS_LOCAL_INDEX_TABLE_PROP_NAME, Boolean.TRUE);
splits = getSplitKeys(connection.getQueryServices().getAllTableRegions(parent.getPhysicalName().getBytes()));
} else {
splits = SchemaUtil.processSplits(splits, pkColumns, saltBucketNum, connection.getQueryServices().getProps().getBoolean(
QueryServices.FORCE_ROW_KEY_ORDER_ATTRIB, QueryServicesOptions.DEFAULT_FORCE_ROW_KEY_ORDER));
}
可以看到上面,如果是一个本地的索引表,则调用如下方法,拿到父的物理数据表,所在那个regionserver中,因为索引表的region要与数据表的region同在一个regionserver上(相同的key区间)
connection.getQueryServices().getAllTableRegions(parent.getPhysicalName().getBytes()
List<HRegionLocation> locations = Lists.newArrayList();
byte[] currentKey = HConstants.EMPTY_START_ROW;
do {
HRegionLocation regionLocation = connection.getRegionLocation(
TableName.valueOf(tableName), currentKey, reload);
locations.add(regionLocation);
currentKey = regionLocation.getRegionInfo().getEndKey();
} while (!Bytes.equals(currentKey, HConstants.EMPTY_END_ROW));
return locations;
上面的是查看该父表具有那些region,
private byte[][] getSplitKeys(List<HRegionLocation> allTableRegions) {
if(allTableRegions.size() == 1) return null;
byte[][] splitKeys = new byte[allTableRegions.size()-1][];
int i = 0;
for (HRegionLocation region : allTableRegions) {
if (region.getRegionInfo().getStartKey().length != 0) {
splitKeys[i] = region.getRegionInfo().getStartKey();
i++;
}
}
return splitKeys;
}
上面方法就可以拿出全部的region的startkey了,就可以知道爷的物理数据表的key的区间情况了
如果不是本地索引表。则调用如下方法
// Given the splits and the rowKeySchema, find out the keys that
public static byte[][] processSplits(byte[][] splits, LinkedHashSet<PColumn> pkColumns, Integer saltBucketNum, boolean defaultRowKeyOrder) throws SQLException {
// FIXME: shouldn't this return if splits.length == 0?
if (splits == null) return null;
// We do not accept user specified splits if the table is salted and we specify defaultRowKeyOrder. In this case,
// throw an exception.
if (splits.length > 0 && saltBucketNum != null && defaultRowKeyOrder) {
throw new SQLExceptionInfo.Builder(SQLExceptionCode.NO_SPLITS_ON_SALTED_TABLE).build().buildException();
}
// If the splits are not specified and table is salted, pre-split the table.
//如果没有指定拆分主键,并且是一个salted类型的表,刚自动生成 saltBucketNum 个预拆分区间
if (splits.length == 0 && saltBucketNum != null) {
splits = SaltingUtil.getSalteByteSplitPoints(saltBucketNum);
}
byte[][] newSplits = new byte[splits.length][];
for (int i=0; i<splits.length; i++) {
newSplits[i] = processSplit(splits[i], pkColumns);
}
return newSplits;
}
//该方法就是很核心的salted类型表的主键前缀拆分的过程了
public static byte[][] getSalteByteSplitPoints(int saltBucketNum) {
byte[][] splits = new byte[saltBucketNum-1][];
for (int i = 1; i < saltBucketNum; i++) {
splits[i-1] = new byte[] {(byte) i};
}
return splits;
}
可以看到,上面先是判断该表是不是索引表,如果是就根据父的物理表的存放当前情况,拿出各个region 的location 的位置信息,拿出该区间的startKey,然后生成splits对象。否则调用 processSplit 方法生成 ,里面进行salt表的判断,如果是就直接生成相应的 saltBucketNum个预拆分区间,以指定char类型的byte对象开头的splits。
总结
- 根据是一个本地索引表,然后根据父的物理表的当前的region location的startkey 进行拆分成同样多的索引区间
- 如果是一个Salte表,根据传进来的saltBucketNum 数量,拆分成指定数量的预拆分region
- 根据外面传进来的slplits进行相应的拆分