Hbase数据库比较适用于写多读少的场景。其查询能力并不突出。
在使用Scan扫描表的时候,很容易踩坑。数据库
踩坑1:在表数据量偏大的状况下(好比上千万数据),执行scan提示60000ms的timeout,而且出现OOM。
分析:缓存
scan对象有几个重要的参数: caching:该值表示一次从RPC请求Client能够从Hbase服务器获取的数据条数。 默认值是-1。按照源码,若是是-1,那么发送给Hbase的值是:HConstants#DEFAULT_HBASE_CLIENT_SCANNER_CACHING,即: Integer.MAX_VALUE。 这是一个很是大的值。  maxResultSize:client从Hbase服务端获取的数据在客户端缓存的最大字节数。 默认是-1。若是使用的是默认值,则缓存大小限制是:2M(即 2 * 1024 * 1024)。  limit:表示一次Scan扫描的行数,至关于MySql的limit。**须要注意的是:此参数值在2.x版本生效,1.x版本没有此参数。** 默认值是-1。若是是默认值,则不会使用该参数。 出现这个问题的缘由是Hbase-client采用的是2.x版本,可是Hbase服务端是1.x版本。使用limit参数对其无效。因此会一次去 服务端查询Integer.MAX_VALUE条数据,致使OOM和timeout。 在构造Hbase服务端的请求参数时,代码分别以下(能够看到2.x多了limitOfRows参数): 2.x版本:
public static ScanRequest buildScanRequest(byte[] regionName, Scan scan, int numberOfRows, boolean closeScanner) throws IOException { ScanRequest.Builder builder = ScanRequest.newBuilder(); RegionSpecifier region = buildRegionSpecifier(RegionSpecifierType.REGION_NAME, regionName); builder.setNumberOfRows(numberOfRows); builder.setCloseScanner(closeScanner); builder.setRegion(region); builder.setScan(ProtobufUtil.toScan(scan)); builder.setClientHandlesPartials(true); builder.setClientHandlesHeartbeats(true); builder.setTrackScanMetrics(scan.isScanMetricsEnabled()); if (scan.getLimit() > 0) { builder.setLimitOfRows(scan.getLimit()); } return builder.build(); }
在1.x版本:服务器
public static ScanRequest buildScanRequest(final byte[] regionName, final Scan scan, final int numberOfRows, final boolean closeScanner) throws IOException { ScanRequest.Builder builder = ScanRequest.newBuilder(); RegionSpecifier region = buildRegionSpecifier( RegionSpecifierType.REGION_NAME, regionName); builder.setNumberOfRows(numberOfRows); builder.setCloseScanner(closeScanner); builder.setRegion(region); builder.setScan(ProtobufUtil.toScan(scan)); builder.setClientHandlesPartials(true); builder.setClientHandlesHeartbeats(true); builder.setTrackScanMetrics(scan.isScanMetricsEnabled()); return builder.build(); }
踩坑2:在进行Scan扫描的时候,随着时间的推移,Scan的速度愈来愈慢。
分析:Scan有2个参数:ui
startRow:扫描的起始rowkey。 filter:值过滤器,好比:RowFilter。 其处理代码以下:
byte[] startRow = scan.getStartRow(); if (startRow != null && startRow.length > 0) { scanBuilder.setStartRow(ByteStringer.wrap(startRow)); } byte[] stopRow = scan.getStopRow(); if (stopRow != null && stopRow.length > 0) { scanBuilder.setStopRow(ByteStringer.wrap(stopRow)); } if (scan.hasFilter()) { scanBuilder.setFilter(ProtobufUtil.toFilter(scan.getFilter())); }
在使用scan进行全表扫描的时候,若是没有指定startRow,那么就会愈来愈慢。由于每次都是从头开始扫描,因此会愈来愈慢。
因此startRow尽可能都要添加上。code