Index: src/main/java/org/apache/hadoop/hbase/client/Scan.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/client/Scan.java (revision 1534494) +++ src/main/java/org/apache/hadoop/hbase/client/Scan.java (working copy) @@ -83,7 +83,10 @@ private static final String RAW_ATTR = "_raw_"; private static final String ONDEMAND_ATTR = "_ondemand_"; private static final String ISOLATION_LEVEL = "_isolationlevel_"; + + /** Scan Hints */ private static final String SMALL_ATTR = "_small_"; + static public final String HINT_NARROW_ROWS = "_hint_narrow_rows_"; private static final byte SCAN_VERSION = (byte)2; private byte [] startRow = HConstants.EMPTY_START_ROW; @@ -525,6 +528,21 @@ } /** + * Indicate to the scanner logic that only small rows are expected. + * Rows are small if there are only a few columns and a few versions, + * and the total row size is around 1-2kb. + * @param narrow + */ + public void setNarrowRows(boolean narrow) { + setAttribute(HINT_NARROW_ROWS, Bytes.toBytes(narrow)); + } + + public boolean hasNarrowRows() { + byte[] attr = getAttribute(HINT_NARROW_ROWS); + return attr == null ? false : Bytes.toBoolean(attr); + } + + /** * Compile the table and column family (i.e. schema) information * into a String. Useful for parsing and aggregation by debugging, * logging, and administration tools. Index: src/main/java/org/apache/hadoop/hbase/regionserver/ExplicitColumnTracker.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/regionserver/ExplicitColumnTracker.java (revision 1534494) +++ src/main/java/org/apache/hadoop/hbase/regionserver/ExplicitColumnTracker.java (working copy) @@ -56,6 +56,9 @@ private final int maxVersions; private final int minVersions; + // hint for the tracker that rows will be small (few columns and few versions) + private final boolean smallRowsHint; + /** * Contains the list of columns that the ExplicitColumnTracker is tracking. * Each ColumnCount instance also tracks how many versions of the requested @@ -79,9 +82,10 @@ * @param ttl The timeToLive to enforce */ public ExplicitColumnTracker(NavigableSet columns, int minVersions, - int maxVersions, long oldestUnexpiredTS) { + int maxVersions, long oldestUnexpiredTS, boolean smallRowsHint) { this.maxVersions = maxVersions; this.minVersions = minVersions; + this.smallRowsHint = smallRowsHint; this.oldestStamp = oldestUnexpiredTS; this.columns = new ArrayList(columns.size()); for(byte [] column : columns) { @@ -135,8 +139,9 @@ if (ret > 0) { // The current KV is smaller than the column the ExplicitColumnTracker - // is interested in, so seek to that column of interest. - return ScanQueryMatcher.MatchCode.SEEK_NEXT_COL; + // is interested in, so skip or seek to that column of interest. + return smallRowsHint ? ScanQueryMatcher.MatchCode.SKIP + : ScanQueryMatcher.MatchCode.SEEK_NEXT_COL; } // The current KV is bigger than the column the ExplicitColumnTracker @@ -166,19 +171,20 @@ return ScanQueryMatcher.MatchCode.SKIP; } int count = this.column.increment(); - if (count >= maxVersions || (count >= minVersions && isExpired(timestamp))) { + if (count > maxVersions || (count > minVersions && isExpired(timestamp))) { // Done with versions for this column ++this.index; resetTS(); if (done()) { // We have served all the requested columns. this.column = null; - return ScanQueryMatcher.MatchCode.INCLUDE_AND_SEEK_NEXT_ROW; + return ScanQueryMatcher.MatchCode.SEEK_NEXT_ROW; } // We are done with current column; advance to next column // of interest. this.column = this.columns.get(this.index); - return ScanQueryMatcher.MatchCode.INCLUDE_AND_SEEK_NEXT_COL; + return smallRowsHint ? ScanQueryMatcher.MatchCode.SKIP + : ScanQueryMatcher.MatchCode.SEEK_NEXT_COL; } setTS(timestamp); return ScanQueryMatcher.MatchCode.INCLUDE; Index: src/main/java/org/apache/hadoop/hbase/regionserver/ScanQueryMatcher.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/regionserver/ScanQueryMatcher.java (revision 1534494) +++ src/main/java/org/apache/hadoop/hbase/regionserver/ScanQueryMatcher.java (working copy) @@ -169,15 +169,15 @@ // use a specialized scan for wildcard column tracker. this.columns = new ScanWildcardColumnTracker( - scanInfo.getMinVersions(), maxVersions, oldestUnexpiredTS); + scanInfo.getMinVersions(), maxVersions, oldestUnexpiredTS, scan.hasNarrowRows()); } else { // whether there is null column in the explicit column query hasNullColumn = (columns.first().length == 0); // We can share the ExplicitColumnTracker, diff is we reset // between rows, not between storefiles. - this.columns = new ExplicitColumnTracker(columns, - scanInfo.getMinVersions(), maxVersions, oldestUnexpiredTS); + this.columns = new ExplicitColumnTracker(columns, scanInfo.getMinVersions(), maxVersions, + oldestUnexpiredTS, scan.hasNarrowRows()); } } Index: src/main/java/org/apache/hadoop/hbase/regionserver/ScanWildcardColumnTracker.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/regionserver/ScanWildcardColumnTracker.java (revision 1534494) +++ src/main/java/org/apache/hadoop/hbase/regionserver/ScanWildcardColumnTracker.java (working copy) @@ -44,6 +44,9 @@ private long oldestStamp; + // hint for the tracker that rows will be small (few columns and few versions) + private final boolean smallRowsHint; + /** * Return maxVersions of every row. * @param minVersion Minimum number of versions to keep @@ -52,10 +55,11 @@ * to the TTL. */ public ScanWildcardColumnTracker(int minVersion, int maxVersion, - long oldestUnexpiredTS) { + long oldestUnexpiredTS, boolean smallRowsHint) { this.maxVersions = maxVersion; this.minVersions = minVersion; this.oldestStamp = oldestUnexpiredTS; + this.smallRowsHint = smallRowsHint; } /** @@ -137,14 +141,17 @@ currentCount++; } if (currentCount > maxVersions) { - return ScanQueryMatcher.MatchCode.SEEK_NEXT_COL; // skip to next col + // skip to next col + return smallRowsHint ? ScanQueryMatcher.MatchCode.SKIP + : ScanQueryMatcher.MatchCode.SEEK_NEXT_COL; } // keep the KV if required by minversions or it is not expired, yet if (currentCount <= minVersions || !isExpired(timestamp)) { setTSAndType(timestamp, type); return ScanQueryMatcher.MatchCode.INCLUDE; } else { - return MatchCode.SEEK_NEXT_COL; + return smallRowsHint ? ScanQueryMatcher.MatchCode.SKIP + : ScanQueryMatcher.MatchCode.SEEK_NEXT_COL; } } Index: src/test/java/org/apache/hadoop/hbase/regionserver/TestExplicitColumnTracker.java =================================================================== --- src/test/java/org/apache/hadoop/hbase/regionserver/TestExplicitColumnTracker.java (revision 1534494) +++ src/test/java/org/apache/hadoop/hbase/regionserver/TestExplicitColumnTracker.java (working copy) @@ -47,7 +47,7 @@ List scannerColumns, List expected) throws IOException { ColumnTracker exp = new ExplicitColumnTracker( - trackColumns, 0, maxVersions, Long.MIN_VALUE); + trackColumns, 0, maxVersions, Long.MIN_VALUE, false); //Initialize result @@ -166,7 +166,7 @@ } ColumnTracker explicit = new ExplicitColumnTracker(columns, 0, maxVersions, - Long.MIN_VALUE); + Long.MIN_VALUE, false); for (int i = 0; i < 100000; i+=2) { byte [] col = Bytes.toBytes("col"+i); ScanQueryMatcher.checkColumn(explicit, col, 0, col.length, 1, KeyValue.Type.Put.getCode(), Index: src/test/java/org/apache/hadoop/hbase/regionserver/TestScanWildcardColumnTracker.java =================================================================== --- src/test/java/org/apache/hadoop/hbase/regionserver/TestScanWildcardColumnTracker.java (revision 1534494) +++ src/test/java/org/apache/hadoop/hbase/regionserver/TestScanWildcardColumnTracker.java (working copy) @@ -36,7 +36,7 @@ public void testCheckColumn_Ok() throws IOException { ScanWildcardColumnTracker tracker = - new ScanWildcardColumnTracker(0, VERSIONS, Long.MIN_VALUE); + new ScanWildcardColumnTracker(0, VERSIONS, Long.MIN_VALUE, false); //Create list of qualifiers List qualifiers = new ArrayList(); @@ -68,7 +68,7 @@ public void testCheckColumn_EnforceVersions() throws IOException { ScanWildcardColumnTracker tracker = - new ScanWildcardColumnTracker(0, VERSIONS, Long.MIN_VALUE); + new ScanWildcardColumnTracker(0, VERSIONS, Long.MIN_VALUE, false); //Create list of qualifiers List qualifiers = new ArrayList(); @@ -101,7 +101,7 @@ public void DisabledTestCheckColumn_WrongOrder() { ScanWildcardColumnTracker tracker = - new ScanWildcardColumnTracker(0, VERSIONS, Long.MIN_VALUE); + new ScanWildcardColumnTracker(0, VERSIONS, Long.MIN_VALUE, false); //Create list of qualifiers List qualifiers = new ArrayList();