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