zoukankan      html  css  js  c++  java
  • HBase Scan Timeout-OutOfOrderScannerNextException

    最近迁移数据时需要执行大Scan,HBase集群经常碰到以下日志:

    Exception in thread "main" org.apache.hadoop.hbase.DoNotRetryIOException: Failed after retry of OutOfOrderScannerNextException: was there a rpc timeout?
    
    

    出现上述日志后这次Scan就挂了,HBase Client不能自动恢复了。下面分析一下相关代码。

    客户端Scan示例如下:

    Scan scan = new Scan();
    scan.setStartRow(...);
    scan.setStopRow(...);
    scan.setCaching(20);
    Result result;
    try (ResultScanner rs = table.getScanner(scan)) {
        while ((result = rs.next()) != null) {
        	// deal with result
        }
    }
    
    1. table.getScanner(scan)做了什么:

      初始化一个ScannerCallable对象,调用call(),这个call()会发送一个特殊的ScanRequest rpc请求给
      数据所在的RS(定位RS的过程不在本文讨论范围),RS收到请求后发现request中没有scanner id,认为这是一个全新的Scan请求,RS会分配一个该RS全局唯一的scanner id,这个id会返回给客户端供这个Scan后续的ScanRequest使用。同时会为这个scan分配一个数据迭代器RegionScannerImpl(具体取数据逻辑看HBase Scan流程分析),将RegionScannerImpl和对应的元数据HRegion包装到RegionScannerHolder中,放入以下map,如下所示:

    protected long addScanner(RegionScanner s, HRegion r) throws LeaseStillHeldException {
    long scannerId = this.scannerIdGen.incrementAndGet();
    String scannerName = String.valueOf(scannerId);

    RegionScannerHolder existing =
      scanners.putIfAbsent(scannerName, new RegionScannerHolder(s, r));
    assert existing == null : "scannerId must be unique within regionserver's whole lifecycle!";
    
    this.leases.createLease(scannerName, this.scannerLeaseTimeoutPeriod,
        new ScannerListener(scannerName));
    
    return scannerId;
    

    }

    
    	最后,RS会为这个Scanner创建一个Lease,有效期60s,这个以后再说,用于控制大Scan无限的占用RS资源。可以看出,这里第一次调ScannerCallable的call(),实际上没有从RS获取到实际数据,而是做了一些初始化工作,例如获取到scanner id.
    
    2. next()做了什么
    
    	第一次next()从RegionServer取回20条记录缓存在client本地,后续19次next直接从本地取,不需要访问RS,第21次继续向RS取20条,如果向RS取数据时,客户端超时了,那么client不会自动从scan成功的最后一个rowkey的下一个rowkey开始取数据。而是抛给上层应用解决。
    	
    	每次向RS获取数据都调用ScannerCallable的call(),由于上面已经获取到了scanner id,这里构造ScanRequest都会带着这个scanner id,并且每次都会返回实际的数据。由于client的一个Scan可能需要多次向RS取数据,为了保证客户端顺序的得到所有数据不漏,Client和RS都维护一个nextCallSeq字段,客户端每次得到RS的一批数据后,将nextCallSeq加1供后续ScanRequest使用。RS端同样,每次接受到ScanRequest都将对应的nextCallSeq加1,如果客户端在每次获取数据超时了,那么client的nextCallSeq没有加1,后续RS收到ScanRequest发现nextCallSeq匹配不上,RS会抛出OutOfOrderScannerNextException,客户端看到这种异常不进行retry,直接抛出next().
    
    碰到这种异常,一个规避的方法就是scan.setCaching()设置小点。另外,一个就是在应用中重试,每次将最后一次Scan得到的最后一个rowkey记下来,一旦出现这种问题,就重新起一个Scan,设置startkey,但是这样的问题是重试后得到的数据是不是一致的:RS端为了维护一次Scan的数据是一致的,在getScanner()里初始化迭代器RegionScannerImpl时将当前的mvcc read point保存了下来,所以如果重启一个新的Scan,read point很可能不一样。
    
    ####参考文献
    [HBase-0.98.9](https://github.com/apache/hbase/tree/0.98)
  • 相关阅读:
    C++STL之vector用法总结
    [ZOJ 4016] Mergable Stack
    [ZOJ 4024] Peak
    关于C++中ios::sync_with_stdio(false)
    [CodeForces 567C] Geometric Progression
    [CodeForce 450A] Jzzhu and Children
    c++的动态绑定和静态绑定及多态的实现原理(摘)
    C++创建对象的3种方式(转载)
    C++虚函数与纯虚函数用法与区别
    C++深入理解虚函数
  • 原文地址:https://www.cnblogs.com/foxmailed/p/4286298.html
Copyright © 2011-2022 走看看