Java应用中创建HBase Region时触发DoNotRetryRegionException错误的求助:仅成功创建1个Region,其余失败
首先咱们来拆解下你遇到的核心问题:
Caused by: org.apache.hadoop.hbase.ipc.RemoteWithExtrasException(org.apache.hadoop.hbase.client.DoNotRetryRegionException): org.apache.hadoop.hbase.client.DoNotRetryRegionException: bc3ec95b447809887e3c198afe4d1084 is not OPEN; regionState={bc3ec95b447809887e3c198afe4d1084 state=CLOSING, ts=1651224527248, server=hbase-docker,16020,1651207907804}
这个报错的本质是:你在循环里反复对同一个正在关闭的Region执行Split操作。HBase执行Region Split时,原Region会先进入CLOSING状态,完成Split后会被移除,同时生成两个新的子Region。而你的代码用了splitRegionAsync异步方法,调用后立刻就去拿最后一个Region(还是那个正在关闭的原Region)并再次发起Split,这时候原Region已经不是OPEN状态,自然就抛出了这个异常。
再看你的代码逻辑:
byte[][] splits = getSplits(countSplits, countSlot); for (byte[] byteSplit : splits) { byte[] regionName = admin.getRegions(tableName).get(admin.getRegions(tableName).size() - 1).getRegionName(); admin.splitRegionAsync(regionName, byteSplit); }
这里存在两个关键问题:
- 异步操作未等待完成:
splitRegionAsync是异步执行的,不会阻塞当前线程,导致下一次循环立刻触发对同一个Region的操作。 - 错误的Region选择逻辑:每次都拿当前表的最后一个Region,但Split完成后原Region会被移除,新的子Region会加入,你的逻辑没有跟踪需要Split的目标Region。
解决方案
针对你的需求,有两种可行的修改方式:
方式一:使用同步Split方法(适合新手,逻辑更直观)
把异步的splitRegionAsync换成同步的splitRegion方法,这样每次Split都会等待操作完成后再进行下一次循环,确保每次操作的Region都是处于OPEN状态的:
byte[][] splits = getSplits(countSplits, countSlot); for (byte[] byteSplit : splits) { // 获取当前表的所有Region,选择最后一个可Split的OPEN状态Region List<RegionInfo> regions = admin.getRegions(tableName); RegionInfo targetRegion = regions.get(regions.size() - 1); // 同步执行Split,等待操作完成 admin.splitRegion(targetRegion.getRegionName(), byteSplit); }
方式二:正确处理异步操作的Future(适合需要异步场景)
如果你必须使用异步操作,需要等待每个splitRegionAsync返回的CompletableFuture完成后,再进行下一次Split,同时更新要操作的Region:
byte[][] splits = getSplits(countSplits, countSlot); for (byte[] byteSplit : splits) { List<RegionInfo> regions = admin.getRegions(tableName); RegionInfo targetRegion = regions.get(regions.size() - 1); // 发起异步Split并等待完成 admin.splitRegionAsync(targetRegion.getRegionName(), byteSplit) .join(); // 等待异步操作完成 }
额外建议
如果你需要批量Split到指定数量的Region,其实可以在创建表的时候直接指定预分区,这样更高效,还能避免后续手动Split带来的集群负载波动:
// 创建表时指定预分区 HTableDescriptor tableDesc = new HTableDescriptor(TableName.valueOf("your_table")); tableDesc.addFamily(new HColumnDescriptor("cf")); admin.createTable(tableDesc, splits); // splits就是你的预分区数组
内容的提问来源于stack exchange,提问作者Dark Morning

