1 /* 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 */ 19 20 package org.apache.hadoop.hbase.client; 21 22 import org.apache.hadoop.classification.InterfaceAudience; 23 import org.apache.hadoop.classification.InterfaceStability; 24 import org.apache.hadoop.hbase.HConstants; 25 import org.apache.hadoop.hbase.filter.Filter; 26 import org.apache.hadoop.hbase.filter.IncompatibleFilterException; 27 import org.apache.hadoop.hbase.io.TimeRange; 28 import org.apache.hadoop.hbase.util.Bytes; 29 30 import java.io.IOException; 31 import java.util.ArrayList; 32 import java.util.HashMap; 33 import java.util.List; 34 import java.util.Map; 35 import java.util.NavigableSet; 36 import java.util.TreeMap; 37 import java.util.TreeSet; 38 39 /** 40 * Used to perform Scan operations. 41 * <p> 42 * All operations are identical to {@link Get} with the exception of 43 * instantiation. Rather than specifying a single row, an optional startRow 44 * and stopRow may be defined. If rows are not specified, the Scanner will 45 * iterate over all rows. 46 * <p> 47 * To scan everything for each row, instantiate a Scan object. 48 * <p> 49 * To modify scanner caching for just this scan, use {@link #setCaching(int) setCaching}. 50 * If caching is NOT set, we will use the caching value of the hosting {@link HTable}. See 51 * {@link HTable#setScannerCaching(int)}. In addition to row caching, it is possible to specify a 52 * maximum result size, using {@link #setMaxResultSize(long)}. When both are used, 53 * single server requests are limited by either number of rows or maximum result size, whichever 54 * limit comes first. 55 * <p> 56 * To further define the scope of what to get when scanning, perform additional 57 * methods as outlined below. 58 * <p> 59 * To get all columns from specific families, execute {@link #addFamily(byte[]) addFamily} 60 * for each family to retrieve. 61 * <p> 62 * To get specific columns, execute {@link #addColumn(byte[], byte[]) addColumn} 63 * for each column to retrieve. 64 * <p> 65 * To only retrieve columns within a specific range of version timestamps, 66 * execute {@link #setTimeRange(long, long) setTimeRange}. 67 * <p> 68 * To only retrieve columns with a specific timestamp, execute 69 * {@link #setTimeStamp(long) setTimestamp}. 70 * <p> 71 * To limit the number of versions of each column to be returned, execute 72 * {@link #setMaxVersions(int) setMaxVersions}. 73 * <p> 74 * To limit the maximum number of values returned for each call to next(), 75 * execute {@link #setBatch(int) setBatch}. 76 * <p> 77 * To add a filter, execute {@link #setFilter(org.apache.hadoop.hbase.filter.Filter) setFilter}. 78 * <p> 79 * Expert: To explicitly disable server-side block caching for this scan, 80 * execute {@link #setCacheBlocks(boolean)}. 81 */ 82 @InterfaceAudience.Public 83 @InterfaceStability.Stable 84 public class Scan extends OperationWithAttributes { 85 private static final String RAW_ATTR = "_raw_"; 86 private static final String ISOLATION_LEVEL = "_isolationlevel_"; 87 88 private byte [] startRow = HConstants.EMPTY_START_ROW; 89 private byte [] stopRow = HConstants.EMPTY_END_ROW; 90 private int maxVersions = 1; 91 private int batch = -1; 92 93 private int storeLimit = -1; 94 private int storeOffset = 0; 95 private boolean getScan; 96 97 // If application wants to collect scan metrics, it needs to 98 // call scan.setAttribute(SCAN_ATTRIBUTES_ENABLE, Bytes.toBytes(Boolean.TRUE)) 99 static public final String SCAN_ATTRIBUTES_METRICS_ENABLE = "scan.attributes.metrics.enable"; 100 static public final String SCAN_ATTRIBUTES_METRICS_DATA = "scan.attributes.metrics.data"; 101 102 // If an application wants to use multiple scans over different tables each scan must 103 // define this attribute with the appropriate table name by calling 104 // scan.setAttribute(Scan.SCAN_ATTRIBUTES_TABLE_NAME, Bytes.toBytes(tableName)) 105 static public final String SCAN_ATTRIBUTES_TABLE_NAME = "scan.attributes.table.name"; 106 107 /* 108 * -1 means no caching 109 */ 110 private int caching = -1; 111 private long maxResultSize = -1; 112 private boolean cacheBlocks = true; 113 private Filter filter = null; 114 private TimeRange tr = new TimeRange(); 115 private Map<byte [], NavigableSet<byte []>> familyMap = 116 new TreeMap<byte [], NavigableSet<byte []>>(Bytes.BYTES_COMPARATOR); 117 private Boolean loadColumnFamiliesOnDemand = null; 118 119 /** 120 * Set it true for small scan to get better performance 121 * 122 * Small scan should use pread and big scan can use seek + read 123 * 124 * seek + read is fast but can cause two problem (1) resource contention (2) 125 * cause too much network io 126 * 127 * [89-fb] Using pread for non-compaction read request 128 * https://issues.apache.org/jira/browse/HBASE-7266 129 * 130 * On the other hand, if setting it true, we would do 131 * openScanner,next,closeScanner in one RPC call. It means the better 132 * performance for small scan. [HBASE-9488]. 133 * 134 * Generally, if the scan range is within one data block(64KB), it could be 135 * considered as a small scan. 136 */ 137 private boolean small = false; 138 139 /** 140 * Create a Scan operation across all rows. 141 */ 142 public Scan() {} 143 144 public Scan(byte [] startRow, Filter filter) { 145 this(startRow); 146 this.filter = filter; 147 } 148 149 /** 150 * Create a Scan operation starting at the specified row. 151 * <p> 152 * If the specified row does not exist, the Scanner will start from the 153 * next closest row after the specified row. 154 * @param startRow row to start scanner at or after 155 */ 156 public Scan(byte [] startRow) { 157 this.startRow = startRow; 158 } 159 160 /** 161 * Create a Scan operation for the range of rows specified. 162 * @param startRow row to start scanner at or after (inclusive) 163 * @param stopRow row to stop scanner before (exclusive) 164 */ 165 public Scan(byte [] startRow, byte [] stopRow) { 166 this.startRow = startRow; 167 this.stopRow = stopRow; 168 //if the startRow and stopRow both are empty, it is not a Get 169 this.getScan = isStartRowAndEqualsStopRow(); 170 } 171 172 /** 173 * Creates a new instance of this class while copying all values. 174 * 175 * @param scan The scan instance to copy from. 176 * @throws IOException When copying the values fails. 177 */ 178 public Scan(Scan scan) throws IOException { 179 startRow = scan.getStartRow(); 180 stopRow = scan.getStopRow(); 181 maxVersions = scan.getMaxVersions(); 182 batch = scan.getBatch(); 183 storeLimit = scan.getMaxResultsPerColumnFamily(); 184 storeOffset = scan.getRowOffsetPerColumnFamily(); 185 caching = scan.getCaching(); 186 maxResultSize = scan.getMaxResultSize(); 187 cacheBlocks = scan.getCacheBlocks(); 188 getScan = scan.isGetScan(); 189 filter = scan.getFilter(); // clone? 190 loadColumnFamiliesOnDemand = scan.getLoadColumnFamiliesOnDemandValue(); 191 TimeRange ctr = scan.getTimeRange(); 192 tr = new TimeRange(ctr.getMin(), ctr.getMax()); 193 Map<byte[], NavigableSet<byte[]>> fams = scan.getFamilyMap(); 194 for (Map.Entry<byte[],NavigableSet<byte[]>> entry : fams.entrySet()) { 195 byte [] fam = entry.getKey(); 196 NavigableSet<byte[]> cols = entry.getValue(); 197 if (cols != null && cols.size() > 0) { 198 for (byte[] col : cols) { 199 addColumn(fam, col); 200 } 201 } else { 202 addFamily(fam); 203 } 204 } 205 for (Map.Entry<String, byte[]> attr : scan.getAttributesMap().entrySet()) { 206 setAttribute(attr.getKey(), attr.getValue()); 207 } 208 } 209 210 /** 211 * Builds a scan object with the same specs as get. 212 * @param get get to model scan after 213 */ 214 public Scan(Get get) { 215 this.startRow = get.getRow(); 216 this.stopRow = get.getRow(); 217 this.filter = get.getFilter(); 218 this.cacheBlocks = get.getCacheBlocks(); 219 this.maxVersions = get.getMaxVersions(); 220 this.storeLimit = get.getMaxResultsPerColumnFamily(); 221 this.storeOffset = get.getRowOffsetPerColumnFamily(); 222 this.tr = get.getTimeRange(); 223 this.familyMap = get.getFamilyMap(); 224 this.getScan = true; 225 } 226 227 public boolean isGetScan() { 228 return this.getScan || isStartRowAndEqualsStopRow(); 229 } 230 231 private boolean isStartRowAndEqualsStopRow() { 232 return this.startRow != null && this.startRow.length > 0 && 233 Bytes.equals(this.startRow, this.stopRow); 234 } 235 /** 236 * Get all columns from the specified family. 237 * <p> 238 * Overrides previous calls to addColumn for this family. 239 * @param family family name 240 * @return this 241 */ 242 public Scan addFamily(byte [] family) { 243 familyMap.remove(family); 244 familyMap.put(family, null); 245 return this; 246 } 247 248 /** 249 * Get the column from the specified family with the specified qualifier. 250 * <p> 251 * Overrides previous calls to addFamily for this family. 252 * @param family family name 253 * @param qualifier column qualifier 254 * @return this 255 */ 256 public Scan addColumn(byte [] family, byte [] qualifier) { 257 NavigableSet<byte []> set = familyMap.get(family); 258 if(set == null) { 259 set = new TreeSet<byte []>(Bytes.BYTES_COMPARATOR); 260 } 261 if (qualifier == null) { 262 qualifier = HConstants.EMPTY_BYTE_ARRAY; 263 } 264 set.add(qualifier); 265 familyMap.put(family, set); 266 return this; 267 } 268 269 /** 270 * Get versions of columns only within the specified timestamp range, 271 * [minStamp, maxStamp). Note, default maximum versions to return is 1. If 272 * your time range spans more than one version and you want all versions 273 * returned, up the number of versions beyond the defaut. 274 * @param minStamp minimum timestamp value, inclusive 275 * @param maxStamp maximum timestamp value, exclusive 276 * @throws IOException if invalid time range 277 * @see #setMaxVersions() 278 * @see #setMaxVersions(int) 279 * @return this 280 */ 281 public Scan setTimeRange(long minStamp, long maxStamp) 282 throws IOException { 283 tr = new TimeRange(minStamp, maxStamp); 284 return this; 285 } 286 287 /** 288 * Get versions of columns with the specified timestamp. Note, default maximum 289 * versions to return is 1. If your time range spans more than one version 290 * and you want all versions returned, up the number of versions beyond the 291 * defaut. 292 * @param timestamp version timestamp 293 * @see #setMaxVersions() 294 * @see #setMaxVersions(int) 295 * @return this 296 */ 297 public Scan setTimeStamp(long timestamp) { 298 try { 299 tr = new TimeRange(timestamp, timestamp+1); 300 } catch(IOException e) { 301 // Will never happen 302 } 303 return this; 304 } 305 306 /** 307 * Set the start row of the scan. 308 * @param startRow row to start scan on (inclusive) 309 * Note: In order to make startRow exclusive add a trailing 0 byte 310 * @return this 311 */ 312 public Scan setStartRow(byte [] startRow) { 313 this.startRow = startRow; 314 return this; 315 } 316 317 /** 318 * Set the stop row. 319 * @param stopRow row to end at (exclusive) 320 * Note: In order to make stopRow inclusive add a trailing 0 byte 321 * @return this 322 */ 323 public Scan setStopRow(byte [] stopRow) { 324 this.stopRow = stopRow; 325 return this; 326 } 327 328 /** 329 * Get all available versions. 330 * @return this 331 */ 332 public Scan setMaxVersions() { 333 this.maxVersions = Integer.MAX_VALUE; 334 return this; 335 } 336 337 /** 338 * Get up to the specified number of versions of each column. 339 * @param maxVersions maximum versions for each column 340 * @return this 341 */ 342 public Scan setMaxVersions(int maxVersions) { 343 this.maxVersions = maxVersions; 344 return this; 345 } 346 347 /** 348 * Set the maximum number of values to return for each call to next() 349 * @param batch the maximum number of values 350 */ 351 public void setBatch(int batch) { 352 if (this.hasFilter() && this.filter.hasFilterRow()) { 353 throw new IncompatibleFilterException( 354 "Cannot set batch on a scan using a filter" + 355 " that returns true for filter.hasFilterRow"); 356 } 357 this.batch = batch; 358 } 359 360 /** 361 * Set the maximum number of values to return per row per Column Family 362 * @param limit the maximum number of values returned / row / CF 363 */ 364 public void setMaxResultsPerColumnFamily(int limit) { 365 this.storeLimit = limit; 366 } 367 368 /** 369 * Set offset for the row per Column Family. 370 * @param offset is the number of kvs that will be skipped. 371 */ 372 public void setRowOffsetPerColumnFamily(int offset) { 373 this.storeOffset = offset; 374 } 375 376 /** 377 * Set the number of rows for caching that will be passed to scanners. 378 * If not set, the default setting from {@link HTable#getScannerCaching()} will apply. 379 * Higher caching values will enable faster scanners but will use more memory. 380 * @param caching the number of rows for caching 381 */ 382 public void setCaching(int caching) { 383 this.caching = caching; 384 } 385 386 /** 387 * @return the maximum result size in bytes. See {@link #setMaxResultSize(long)} 388 */ 389 public long getMaxResultSize() { 390 return maxResultSize; 391 } 392 393 /** 394 * Set the maximum result size. The default is -1; this means that no specific 395 * maximum result size will be set for this scan, and the global configured 396 * value will be used instead. (Defaults to unlimited). 397 * 398 * @param maxResultSize The maximum result size in bytes. 399 */ 400 public void setMaxResultSize(long maxResultSize) { 401 this.maxResultSize = maxResultSize; 402 } 403 404 /** 405 * Apply the specified server-side filter when performing the Scan. 406 * @param filter filter to run on the server 407 * @return this 408 */ 409 public Scan setFilter(Filter filter) { 410 this.filter = filter; 411 return this; 412 } 413 414 /** 415 * Setting the familyMap 416 * @param familyMap map of family to qualifier 417 * @return this 418 */ 419 public Scan setFamilyMap(Map<byte [], NavigableSet<byte []>> familyMap) { 420 this.familyMap = familyMap; 421 return this; 422 } 423 424 /** 425 * Getting the familyMap 426 * @return familyMap 427 */ 428 public Map<byte [], NavigableSet<byte []>> getFamilyMap() { 429 return this.familyMap; 430 } 431 432 /** 433 * @return the number of families in familyMap 434 */ 435 public int numFamilies() { 436 if(hasFamilies()) { 437 return this.familyMap.size(); 438 } 439 return 0; 440 } 441 442 /** 443 * @return true if familyMap is non empty, false otherwise 444 */ 445 public boolean hasFamilies() { 446 return !this.familyMap.isEmpty(); 447 } 448 449 /** 450 * @return the keys of the familyMap 451 */ 452 public byte[][] getFamilies() { 453 if(hasFamilies()) { 454 return this.familyMap.keySet().toArray(new byte[0][0]); 455 } 456 return null; 457 } 458 459 /** 460 * @return the startrow 461 */ 462 public byte [] getStartRow() { 463 return this.startRow; 464 } 465 466 /** 467 * @return the stoprow 468 */ 469 public byte [] getStopRow() { 470 return this.stopRow; 471 } 472 473 /** 474 * @return the max number of versions to fetch 475 */ 476 public int getMaxVersions() { 477 return this.maxVersions; 478 } 479 480 /** 481 * @return maximum number of values to return for a single call to next() 482 */ 483 public int getBatch() { 484 return this.batch; 485 } 486 487 /** 488 * @return maximum number of values to return per row per CF 489 */ 490 public int getMaxResultsPerColumnFamily() { 491 return this.storeLimit; 492 } 493 494 /** 495 * Method for retrieving the scan's offset per row per column 496 * family (#kvs to be skipped) 497 * @return row offset 498 */ 499 public int getRowOffsetPerColumnFamily() { 500 return this.storeOffset; 501 } 502 503 /** 504 * @return caching the number of rows fetched when calling next on a scanner 505 */ 506 public int getCaching() { 507 return this.caching; 508 } 509 510 /** 511 * @return TimeRange 512 */ 513 public TimeRange getTimeRange() { 514 return this.tr; 515 } 516 517 /** 518 * @return RowFilter 519 */ 520 public Filter getFilter() { 521 return filter; 522 } 523 524 /** 525 * @return true is a filter has been specified, false if not 526 */ 527 public boolean hasFilter() { 528 return filter != null; 529 } 530 531 /** 532 * Set whether blocks should be cached for this Scan. 533 * <p> 534 * This is true by default. When true, default settings of the table and 535 * family are used (this will never override caching blocks if the block 536 * cache is disabled for that family or entirely). 537 * 538 * @param cacheBlocks if false, default settings are overridden and blocks 539 * will not be cached 540 */ 541 public void setCacheBlocks(boolean cacheBlocks) { 542 this.cacheBlocks = cacheBlocks; 543 } 544 545 /** 546 * Get whether blocks should be cached for this Scan. 547 * @return true if default caching should be used, false if blocks should not 548 * be cached 549 */ 550 public boolean getCacheBlocks() { 551 return cacheBlocks; 552 } 553 554 /** 555 * Set the value indicating whether loading CFs on demand should be allowed (cluster 556 * default is false). On-demand CF loading doesn't load column families until necessary, e.g. 557 * if you filter on one column, the other column family data will be loaded only for the rows 558 * that are included in result, not all rows like in normal case. 559 * With column-specific filters, like SingleColumnValueFilter w/filterIfMissing == true, 560 * this can deliver huge perf gains when there's a cf with lots of data; however, it can 561 * also lead to some inconsistent results, as follows: 562 * - if someone does a concurrent update to both column families in question you may get a row 563 * that never existed, e.g. for { rowKey = 5, { cat_videos => 1 }, { video => "my cat" } } 564 * someone puts rowKey 5 with { cat_videos => 0 }, { video => "my dog" }, concurrent scan 565 * filtering on "cat_videos == 1" can get { rowKey = 5, { cat_videos => 1 }, 566 * { video => "my dog" } }. 567 * - if there's a concurrent split and you have more than 2 column families, some rows may be 568 * missing some column families. 569 */ 570 public void setLoadColumnFamiliesOnDemand(boolean value) { 571 this.loadColumnFamiliesOnDemand = value; 572 } 573 574 /** 575 * Get the raw loadColumnFamiliesOnDemand setting; if it's not set, can be null. 576 */ 577 public Boolean getLoadColumnFamiliesOnDemandValue() { 578 return this.loadColumnFamiliesOnDemand; 579 } 580 581 /** 582 * Get the logical value indicating whether on-demand CF loading should be allowed. 583 */ 584 public boolean doLoadColumnFamiliesOnDemand() { 585 return (this.loadColumnFamiliesOnDemand != null) 586 && this.loadColumnFamiliesOnDemand.booleanValue(); 587 } 588 589 /** 590 * Compile the table and column family (i.e. schema) information 591 * into a String. Useful for parsing and aggregation by debugging, 592 * logging, and administration tools. 593 * @return Map 594 */ 595 @Override 596 public Map<String, Object> getFingerprint() { 597 Map<String, Object> map = new HashMap<String, Object>(); 598 List<String> families = new ArrayList<String>(); 599 if(this.familyMap.size() == 0) { 600 map.put("families", "ALL"); 601 return map; 602 } else { 603 map.put("families", families); 604 } 605 for (Map.Entry<byte [], NavigableSet<byte[]>> entry : 606 this.familyMap.entrySet()) { 607 families.add(Bytes.toStringBinary(entry.getKey())); 608 } 609 return map; 610 } 611 612 /** 613 * Compile the details beyond the scope of getFingerprint (row, columns, 614 * timestamps, etc.) into a Map along with the fingerprinted information. 615 * Useful for debugging, logging, and administration tools. 616 * @param maxCols a limit on the number of columns output prior to truncation 617 * @return Map 618 */ 619 @Override 620 public Map<String, Object> toMap(int maxCols) { 621 // start with the fingerpring map and build on top of it 622 Map<String, Object> map = getFingerprint(); 623 // map from families to column list replaces fingerprint's list of families 624 Map<String, List<String>> familyColumns = 625 new HashMap<String, List<String>>(); 626 map.put("families", familyColumns); 627 // add scalar information first 628 map.put("startRow", Bytes.toStringBinary(this.startRow)); 629 map.put("stopRow", Bytes.toStringBinary(this.stopRow)); 630 map.put("maxVersions", this.maxVersions); 631 map.put("batch", this.batch); 632 map.put("caching", this.caching); 633 map.put("maxResultSize", this.maxResultSize); 634 map.put("cacheBlocks", this.cacheBlocks); 635 map.put("loadColumnFamiliesOnDemand", this.loadColumnFamiliesOnDemand); 636 List<Long> timeRange = new ArrayList<Long>(); 637 timeRange.add(this.tr.getMin()); 638 timeRange.add(this.tr.getMax()); 639 map.put("timeRange", timeRange); 640 int colCount = 0; 641 // iterate through affected families and list out up to maxCols columns 642 for (Map.Entry<byte [], NavigableSet<byte[]>> entry : 643 this.familyMap.entrySet()) { 644 List<String> columns = new ArrayList<String>(); 645 familyColumns.put(Bytes.toStringBinary(entry.getKey()), columns); 646 if(entry.getValue() == null) { 647 colCount++; 648 --maxCols; 649 columns.add("ALL"); 650 } else { 651 colCount += entry.getValue().size(); 652 if (maxCols <= 0) { 653 continue; 654 } 655 for (byte [] column : entry.getValue()) { 656 if (--maxCols <= 0) { 657 continue; 658 } 659 columns.add(Bytes.toStringBinary(column)); 660 } 661 } 662 } 663 map.put("totalColumns", colCount); 664 if (this.filter != null) { 665 map.put("filter", this.filter.toString()); 666 } 667 // add the id if set 668 if (getId() != null) { 669 map.put("id", getId()); 670 } 671 return map; 672 } 673 674 /** 675 * Enable/disable "raw" mode for this scan. 676 * If "raw" is enabled the scan will return all 677 * delete marker and deleted rows that have not 678 * been collected, yet. 679 * This is mostly useful for Scan on column families 680 * that have KEEP_DELETED_ROWS enabled. 681 * It is an error to specify any column when "raw" is set. 682 * @param raw True/False to enable/disable "raw" mode. 683 */ 684 public void setRaw(boolean raw) { 685 setAttribute(RAW_ATTR, Bytes.toBytes(raw)); 686 } 687 688 /** 689 * @return True if this Scan is in "raw" mode. 690 */ 691 public boolean isRaw() { 692 byte[] attr = getAttribute(RAW_ATTR); 693 return attr == null ? false : Bytes.toBoolean(attr); 694 } 695 696 /* 697 * Set the isolation level for this scan. If the 698 * isolation level is set to READ_UNCOMMITTED, then 699 * this scan will return data from committed and 700 * uncommitted transactions. If the isolation level 701 * is set to READ_COMMITTED, then this scan will return 702 * data from committed transactions only. If a isolation 703 * level is not explicitly set on a Scan, then it 704 * is assumed to be READ_COMMITTED. 705 * @param level IsolationLevel for this scan 706 */ 707 public void setIsolationLevel(IsolationLevel level) { 708 setAttribute(ISOLATION_LEVEL, level.toBytes()); 709 } 710 /* 711 * @return The isolation level of this scan. 712 * If no isolation level was set for this scan object, 713 * then it returns READ_COMMITTED. 714 * @return The IsolationLevel for this scan 715 */ 716 public IsolationLevel getIsolationLevel() { 717 byte[] attr = getAttribute(ISOLATION_LEVEL); 718 return attr == null ? IsolationLevel.READ_COMMITTED : 719 IsolationLevel.fromBytes(attr); 720 } 721 722 /** 723 * Set whether this scan is a small scan 724 * <p> 725 * Small scan should use pread and big scan can use seek + read 726 * 727 * seek + read is fast but can cause two problem (1) resource contention (2) 728 * cause too much network io 729 * 730 * [89-fb] Using pread for non-compaction read request 731 * https://issues.apache.org/jira/browse/HBASE-7266 732 * 733 * On the other hand, if setting it true, we would do 734 * openScanner,next,closeScanner in one RPC call. It means the better 735 * performance for small scan. [HBASE-9488]. 736 * 737 * Generally, if the scan range is within one data block(64KB), it could be 738 * considered as a small scan. 739 * 740 * @param small 741 */ 742 public void setSmall(boolean small) { 743 this.small = small; 744 } 745 746 /** 747 * Get whether this scan is a small scan 748 * @return true if small scan 749 */ 750 public boolean isSmall() { 751 return small; 752 } 753 }