diff --git a/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java b/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java index 88a78a7..ebcf0f3 100644 --- a/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java +++ b/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java @@ -1413,7 +1413,18 @@ public class HRegion implements HeapSize { // , Writable{ if (batchOp.retCodes[i] != OperationStatusCode.NOT_RUN) continue; Put p = batchOp.operations[i].getFirst(); - addedSize += applyFamilyMapToMemstore(p.getFamilyMap()); + if (walEdit != null && walEdit.getKeyValues().size() == 1) { + KeyValue kv = walEdit.getKeyValues().get(0); + // Only 1 KV, does the user care about versions? If no, update in-place + if (regionInfo.getTableDesc().getFamily(kv.getFamily()).getMaxVersions() == 1) { + addedSize = getStore(kv.getFamily()).updateColumnValue(kv.getRow(), + kv.getFamily(), kv.getQualifier(), kv.getValue()); + } else { + addedSize = applyFamilyMapToMemstore(p.getFamilyMap()); + } + } else { + addedSize = applyFamilyMapToMemstore(p.getFamilyMap()); + } batchOp.retCodes[i] = OperationStatusCode.SUCCESS; } success = true; @@ -1618,19 +1629,32 @@ public class HRegion implements HeapSize { // , Writable{ try { checkFamilies(familyMap.keySet()); updateKVTimestamps(familyMap.values(), byteNow); + WALEdit walEdit = null; // write/sync to WAL should happen before we touch memstore. // // If order is reversed, i.e. we write to memstore first, and // for some reason fail to write/sync to commit log, the memstore // will contain uncommitted transactions. if (writeToWAL) { - WALEdit walEdit = new WALEdit(); + walEdit = new WALEdit(); addFamilyMapToWALEdit(familyMap, walEdit); this.log.append(regionInfo, regionInfo.getTableDesc().getName(), walEdit, now); } - - long addedSize = applyFamilyMapToMemstore(familyMap); + long addedSize = 0; + // Check if this is an occasion for in-place update + if (walEdit != null && walEdit.getKeyValues().size() == 1) { + KeyValue kv = walEdit.getKeyValues().get(0); + // Only 1 KV, does the user care about versions? If no, update in-place + if (regionInfo.getTableDesc().getFamily(kv.getFamily()).getMaxVersions() == 1) { + addedSize = getStore(kv.getFamily()).updateColumnValue(kv.getRow(), + kv.getFamily(), kv.getQualifier(), kv.getValue()); + } else { + addedSize = applyFamilyMapToMemstore(familyMap); + } + } else { + addedSize = applyFamilyMapToMemstore(familyMap); + } flush = isFlushSize(memstoreSize.addAndGet(addedSize)); } finally { this.updatesLock.readLock().unlock(); diff --git a/src/main/java/org/apache/hadoop/hbase/regionserver/MemStore.java b/src/main/java/org/apache/hadoop/hbase/regionserver/MemStore.java index 2325f71..fffe7f5 100644 --- a/src/main/java/org/apache/hadoop/hbase/regionserver/MemStore.java +++ b/src/main/java/org/apache/hadoop/hbase/regionserver/MemStore.java @@ -364,7 +364,7 @@ public class MemStore implements HeapSize { public long updateColumnValue(byte[] row, byte[] family, byte[] qualifier, - long newValue, + byte[] newValue, long now) { this.lock.readLock().lock(); try { @@ -413,9 +413,7 @@ public class MemStore implements HeapSize { // add the new value now. this might have the same TS as an existing KV, thus confusing // readers slightly for a MOMENT until we erase the old one (and thus old value). - newKv = new KeyValue(row, family, qualifier, - now, - Bytes.toBytes(newValue)); + newKv = new KeyValue(row, family, qualifier, now, newValue); long addedSize = add(newKv); // remove extra versions. diff --git a/src/main/java/org/apache/hadoop/hbase/regionserver/Store.java b/src/main/java/org/apache/hadoop/hbase/regionserver/Store.java index c542bfc..25eb386 100644 --- a/src/main/java/org/apache/hadoop/hbase/regionserver/Store.java +++ b/src/main/java/org/apache/hadoop/hbase/regionserver/Store.java @@ -1296,6 +1296,26 @@ public class Store implements HeapSize { public long updateColumnValue(byte [] row, byte [] f, byte [] qualifier, long newValue) throws IOException { + return updateColumnValue(row, f, qualifier, Bytes.toBytes(newValue)); + } + + /** + * Increments the value for the given row/family/qualifier. + * + * This function will always be seen as atomic by other readers + * because it only puts a single KV to memstore. Thus no + * read/write control necessary. + * + * @param row + * @param f + * @param qualifier + * @param newValue the new value to set into memstore + * @return memstore size delta + * @throws IOException + */ + public long updateColumnValue(byte [] row, byte [] f, + byte [] qualifier, byte [] newValue) + throws IOException { this.lock.readLock().lock(); try {