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