Hbase數據庫比較適用於寫多讀少的場景。其查詢能力並不突出。
在使用Scan掃描表的時候,很容易踩坑。數據庫
踩坑1:在表數據量偏大的狀況下(好比上千萬數據),執行scan提示60000ms的timeout,而且出現OOM。
分析:緩存
scan對象有幾個重要的參數: caching:該值表示一次從RPC請求Client能夠從Hbase服務器獲取的數據條數。 默認值是-1。按照源碼,若是是-1,那麼發送給Hbase的值是:HConstants#DEFAULT_HBASE_CLIENT_SCANNER_CACHING,即: Integer.MAX_VALUE。 這是一個很是大的值。 ![image.png](/img/bVcRFNj) maxResultSize:client從Hbase服務端獲取的數據在客戶端緩存的最大字節數。 默認是-1。若是使用的是默認值,則緩存大小限制是:2M(即 2 * 1024 * 1024)。 ![image.png](/img/bVcRFNg) 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