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