HBase分析之Get、Scan(一)
来源:互联网 发布:mac如何下载eclipse 编辑:程序博客网 时间:2024/05/18 15:06
Get
Get操作调用的是RSRpcServices的get方法,调用过程首先找到包含数据的Region,然后从这个Region中获取需要的数据。
public GetResponse get(final RpcController controller, final GetRequest request) throws ServiceException { ... try { ... Region region = getRegion(request.getRegion()); ... if (get.hasClosestRowBefore() && get.getClosestRowBefore()) { ... r = region.getClosestRowBefore(row, family); } else { ... r = region.get(clientGet); ... } ... return builder.build(); ...}
region.getClosestRowBefore实际也是调用的region.get方法,所以直接看get方法
public Result get(final Get get) throws IOException { checkRow(get.getRow(), "Get"); // Verify families are all valid if (get.hasFamilies()) { for (byte [] family: get.familySet()) { checkFamily(family); } } else { // Adding all families to scanner for (byte[] family: this.htableDescriptor.getFamiliesKeys()) { get.addFamily(family); } } List<Cell> results = get(get, true); boolean stale = this.getRegionInfo().getReplicaId() != 0; return Result.create(results, get.isCheckExistenceOnly() ? !results.isEmpty() : null, stale);}
这个方法特别有意思,如果这个请求里包含了family,就检查下这些family是否存在;如果这个请求里不包含family,就把这个表里所有的family都加到这个请求里。所以不设置family时,就会遍历所有的family来找到需要的数据。然后看List<Cell> results = get(get, true);
,这个方法创建了一个Scan,调用了Scan,所以要了解Get,还看Scan!
public List<Cell> get(Get get, boolean withCoprocessor) throws IOException { ... Scan scan = new Scan(get); RegionScanner scanner = null; try { scanner = getScanner(scan); scanner.next(results); } ... return results;}
Scan
又是一段400多行的代码,完整代码就不贴了,有兴趣可以阅读下源码,在RSRpcServices#scan方法中。
整理一下代码的逻辑,共处理了Scan过程中3个阶段的操作:
- 获得scanner id,签订租约Leases
- 扫描获取数据,续约Leases
- 再次请求,确认数据扫描已经完成
第一步,获得scanner id,签订租约Leases。
代码中依靠scanner id来判断是否已经完成了第一阶段,如果没有完成,就查找到数据所在的Region,创建一个scanner,并把这个scanner添加到缓存中。(查找Region的过程就是从map里取一个Region,这个在Put一节中已经讲过了)
if (request.hasScannerId()) { ...} else { region = getRegion(request.getRegion()); ... if (!scan.hasFamilies()) { // Adding all families to scanner for (byte[] family: region.getTableDesc().getFamiliesKeys()) { scan.addFamily(family); } } ... if (scanner == null) { scanner = region.getScanner(scan); } ... scannerId = addScanner(scanner, region); scannerName = String.valueOf(scannerId); ttl = this.scannerLeaseTimeoutPeriod;}
在scannerId = addScanner(scanner, region);
方法中,scan与Region Server签订了租约,表示scanner会其缓存多长时间。通过hbase.client.scanner.timeout.perio
参数设置,默认情况下为60000ms,即一分钟。租约是一个异步线程,通过线程sleep,等待到租约到期,然后清除缓存。
public void createLease(String leaseName, int leaseTimeoutPeriod, final LeaseListener listener) throws LeaseStillHeldException { addLease(new Lease(leaseName, leaseTimeoutPeriod, listener));}
然后设置上ttl、scanner id、moreResults(这里的moreResults为初始值true),就返回了
if (ttl > 0) { builder.setTtl(ttl);}builder.setScannerId(scannerId);builder.setMoreResults(moreResults);return builder.build();
第二步,扫描获取数据,签订租约Leases
如果超出了租约时间继续请求,那就会抛出错误;如果在租约时间内继续请求了,那么就到了第二阶段,扫描获取数据。
首先从request中获得scanner id。
long scannerId = -1;if (request.hasScannerId()) { scannerId = request.getScannerId(); scannerName = String.valueOf(scannerId);}
然后从缓存中获取缓存的scanner,这里就是第一步的if中省略的代码
if (request.hasScannerId()) { rsh = scanners.get(scannerName); scanner = rsh.s; HRegionInfo hri = scanner.getRegionInfo(); region = regionServer.getRegion(hri.getRegionName());}
然后将租约移除,因为租约是异步的,所以很可能在执行过程中过期了,还是先移除掉。
lease = regionServer.leases.removeLease(scannerName);
然后获取数据,循环调用scanner.nextRaw方法获取数据,获取到的数据先存入values,转换完成后放入results中。如果results中的数量达到了上限或者没有更多数据了,就不再获取了,break出来。scanner.nextRaw方法比较复杂,这里涉及到多个Put版本、Delete的数据扫描,需要再开一篇来讲。
List<Result> results = new ArrayList<Result>();...while (i < rows) { ... moreRows = scanner.nextRaw(values, scannerContext); if (!values.isEmpty()) { final boolean partial = scannerContext.partialResultFormed(); Result r = Result.create(values, null, stale, partial); lastBlock = addSize(context, r, lastBlock); results.add(r); i++; } if (limitReached || !moreRows) { break; }}
经过一轮扫描,如果没有更多数据、或者达到了一次请求的上限,就把已经取到的数据results放进builder返回。然后续签一个新的租约,租约时长还是1分钟。
if (scanner.isFilterDone() && results.isEmpty()) { // 如果扫描完成,这就是第三步的事情了} else { // 没完成,就把数据放进builder返回 addResults(builder, results, controller, RegionReplicaUtil.isDefaultReplica(region.getRegionInfo()));}// 续约Leases,addLease方法中会重新刷新租期if (scanners.containsKey(scannerName)) { if (lease != null) regionServer.leases.addLease(lease); ttl = this.scannerLeaseTimeoutPeriod;}
第三步,再次请求,确认数据扫描已经完成
和第二步干的事情一样,只不过这次发现扫描完成了
if (scanner.isFilterDone() && results.isEmpty()) { moreResults = false; results = null;}
当moreResults为false时,就会关闭缓存的scanner,closeScanner方法中会将租约移除,这样占用的资源就释放完了,就可以返回没有results的builder,确认扫描完成了。
if (!moreResults || closeScanner) { ttl = 0; moreResults = false; closeScanner(region, scanner, scannerName);}
好了,Get、Scan的流程看完了,下篇看scanner.nextRaw方法
-END-
- HBase分析之Get、Scan(一)
- HBase分析之Get、Scan(二)RegionScanner
- HBase分析之Get、Scan(三)StoreScanner
- HBase的scan源码分析客户端部分之整体流程(一)
- Hbase 之 scan
- hbase源码学习之GET操作之get转化为scan
- HBase scan的客户端分析
- hbase:单机环境搭建、hbase表操作示例(create, list, put, get, scan, disable, drop...)
- hbase学习记录之scan
- hbase源码分析HTable ->getScanner(final Scan scan)源码分析
- Hbase源码研究(二)-------get最终转化为scan来处理(1)
- Hbase源码研究(三)-------get最终转化为scan来处理(2)
- hbase源码系列(十二)Get、Scan在服务端是如何处理
- HBase源码系列(五)Get、Scan在服务端是如何处理的?
- HBase 0.92.1 Scan 源码详细分析
- HBase的Scan实现源码分析
- go-hbase的Scan模型源码分析
- Hbase scan
- Remove Duplicates from Sorted Array
- 【SSM】WEB项目中的中文乱码问题
- 【2】git基本用法(上)
- USACO 丢失的牛
- 在XAMPP上运行一个组合的Jekyll / PHP站点
- HBase分析之Get、Scan(一)
- CDQZ Challenge 16
- HDU 5952
- 代码论千言:如何评价别人的代码
- 移动架构15_解释器模式
- python学习之Turtle 1
- 文章标题
- 关于 Java 数组的 12 个最佳方法
- Qt串口接收数据长度不稳定问题