Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HBASE-29039 Optimize read performance for accumulated delete markers on the same row or cell #6557

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -636,10 +636,11 @@ public boolean next(List<? super ExtendedCell> outResult, ScannerContext scanner
scannerContext.incrementBlockProgress(blockSize);
});

prevCell = cell;
scannerContext.setLastPeekedCell(cell);
topChanged = false;
ScanQueryMatcher.MatchCode qcode = matcher.match(cell);
ScanQueryMatcher.MatchCode qcode = matcher.match(cell, prevCell);
LOG.trace("next - cell={}, prevCell={}, qCode={}", cell, prevCell, qcode);
prevCell = cell;
switch (qcode) {
case INCLUDE:
case INCLUDE_AND_SEEK_NEXT_ROW:
Expand Down Expand Up @@ -757,6 +758,12 @@ public boolean next(List<? super ExtendedCell> outResult, ScannerContext scanner
if (stateAfterSeekNextColumn != null) {
return scannerContext.setScannerState(stateAfterSeekNextColumn).hasMoreValues();
}
// for skipping delete markers
if (
CellUtil.isDelete(cell) && this.heap.peek() != null && this.heap.peek().equals(cell)
) {
this.heap.next();
}
break;

case SKIP:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package org.apache.hadoop.hbase.regionserver.querymatcher;

import java.io.IOException;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.ExtendedCell;
import org.apache.hadoop.hbase.KeepDeletedCells;
import org.apache.hadoop.hbase.PrivateCellUtil;
Expand All @@ -40,12 +41,15 @@ public abstract class NormalUserScanQueryMatcher extends UserScanQueryMatcher {
/** whether time range queries can see rows "behind" a delete */
protected final boolean seePastDeleteMarkers;

private final int scanMaxVersions;

protected NormalUserScanQueryMatcher(Scan scan, ScanInfo scanInfo, ColumnTracker columns,
boolean hasNullColumn, DeleteTracker deletes, long oldestUnexpiredTS, long now) {
super(scan, scanInfo, columns, hasNullColumn, oldestUnexpiredTS, now);
this.deletes = deletes;
this.get = scan.isGetScan();
this.seePastDeleteMarkers = scanInfo.getKeepDeletedCells() != KeepDeletedCells.FALSE;
this.scanMaxVersions = scan.getMaxVersions();
}

@Override
Expand All @@ -56,11 +60,16 @@ public void beforeShipped() throws IOException {

@Override
public MatchCode match(ExtendedCell cell) throws IOException {
return match(cell, null);
}

@Override
public MatchCode match(ExtendedCell cell, ExtendedCell prevCell) throws IOException {
if (filter != null && filter.filterAllRemaining()) {
return MatchCode.DONE_SCAN;
}
MatchCode returnCode = preCheck(cell);
if (returnCode != null) {
MatchCode returnCode;
if ((returnCode = preCheck(cell)) != null) {
return returnCode;
}
long timestamp = cell.getTimestamp();
Expand All @@ -71,15 +80,55 @@ public MatchCode match(ExtendedCell cell) throws IOException {
if (includeDeleteMarker) {
this.deletes.add(cell);
}
// optimization for delete markers
if ((returnCode = checkCanSeekNextCol(cell, prevCell)) != null) {
return returnCode;
}
return MatchCode.SKIP;
}
returnCode = checkDeleted(deletes, cell);
if (returnCode != null) {
// optimization when prevCell is Delete or DeleteFamilyVersion
if ((returnCode = checkDeletedEffectively(cell, prevCell)) != null) {
return returnCode;
}
if ((returnCode = checkDeleted(deletes, cell)) != null) {
return returnCode;
}
return matchColumn(cell, timestamp, typeByte);
}

private MatchCode checkCanSeekNextCol(ExtendedCell cell, ExtendedCell prevCell) {
// optimization for DeleteFamily and DeleteColumn(only for empty qualifier)
if (
canOptimizeReadDeleteMarkers() && (PrivateCellUtil.isDeleteFamily(cell)
|| PrivateCellUtil.isDeleteColumns(cell) && cell.getQualifierLength() > 0)
) {
return MatchCode.SEEK_NEXT_COL;
}
// optimization for duplicate Delete and DeleteFamilyVersion
return checkDeletedEffectively(cell, prevCell);
}

// If prevCell is a delete marker and cell is a Put or delete marker,
// it means the cell is deleted effectively.
// And we can do SEEK_NEXT_COL.
private MatchCode checkDeletedEffectively(ExtendedCell cell, ExtendedCell prevCell) {
if (
prevCell != null && canOptimizeReadDeleteMarkers()
&& CellUtil.matchingRowColumn(prevCell, cell) && CellUtil.matchingTimestamp(prevCell, cell)
&& (PrivateCellUtil.isDeleteType(prevCell) && cell.getQualifierLength() > 0
|| PrivateCellUtil.isDeleteFamilyVersion(prevCell))
) {
return MatchCode.SEEK_NEXT_COL;
}
return null;
}

private boolean canOptimizeReadDeleteMarkers() {
// for simplicity, optimization works only for these cases
return !seePastDeleteMarkers && scanMaxVersions == 1;
}

@Override
protected void reset() {
deletes.reset();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,25 @@ protected final MatchCode checkDeleted(DeleteTracker deletes, ExtendedCell cell)
*/
public abstract MatchCode match(ExtendedCell cell) throws IOException;

/**
* Determines if the caller should do one of several things:
* <ul>
* <li>seek/skip to the next row (MatchCode.SEEK_NEXT_ROW)</li>
* <li>seek/skip to the next column (MatchCode.SEEK_NEXT_COL)</li>
* <li>include the current KeyValue (MatchCode.INCLUDE)</li>
* <li>ignore the current KeyValue (MatchCode.SKIP)</li>
* <li>got to the next row (MatchCode.DONE)</li>
* </ul>
* @param cell KeyValue to check
* @param prevCell KeyValue checked previously
* @return The match code instance.
* @throws IOException in case there is an internal consistency problem caused by a data
* corruption.
*/
public MatchCode match(ExtendedCell cell, ExtendedCell prevCell) throws IOException {
return match(cell);
}

/** Returns the start key */
public ExtendedCell getStartKey() {
return startKey;
Expand Down Expand Up @@ -284,7 +303,8 @@ public ExtendedCell getKeyForNextColumn(ExtendedCell cell) {
// see HBASE-18471 for more details
// see TestFromClientSide3#testScanAfterDeletingSpecifiedRow
// see TestFromClientSide3#testScanAfterDeletingSpecifiedRowV2
if (cell.getQualifierLength() == 0) {
// But we can seek to next column if the cell is a type of DeleteFamily.
if (cell.getQualifierLength() == 0 && !PrivateCellUtil.isDeleteFamily(cell)) {
ExtendedCell nextKey = PrivateCellUtil.createNextOnRowCol(cell);
if (nextKey != cell) {
return nextKey;
Expand Down
Loading