You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by li...@apache.org on 2014/01/11 02:30:02 UTC

svn commit: r1557298 - in /hbase/branches/0.98/hbase-server/src: main/java/org/apache/hadoop/hbase/io/hfile/LruBlockCache.java test/java/org/apache/hadoop/hbase/io/hfile/TestLruBlockCache.java

Author: liangxie
Date: Sat Jan 11 01:30:02 2014
New Revision: 1557298

URL: http://svn.apache.org/r1557298
Log:
HBASE-10263 make LruBlockCache single/multi/in-memory ratio user-configurable and provide preemptive mode for in-memory type block (Feng Honghua)

Modified:
    hbase/branches/0.98/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/LruBlockCache.java
    hbase/branches/0.98/hbase-server/src/test/java/org/apache/hadoop/hbase/io/hfile/TestLruBlockCache.java

Modified: hbase/branches/0.98/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/LruBlockCache.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.98/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/LruBlockCache.java?rev=1557298&r1=1557297&r2=1557298&view=diff
==============================================================================
--- hbase/branches/0.98/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/LruBlockCache.java (original)
+++ hbase/branches/0.98/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/LruBlockCache.java Sat Jan 11 01:30:02 2014
@@ -101,6 +101,16 @@ public class LruBlockCache implements Bl
 
   static final String LRU_MIN_FACTOR_CONFIG_NAME = "hbase.lru.blockcache.min.factor";
   static final String LRU_ACCEPTABLE_FACTOR_CONFIG_NAME = "hbase.lru.blockcache.acceptable.factor";
+  static final String LRU_SINGLE_PERCENTAGE_CONFIG_NAME = "hbase.lru.blockcache.single.percentage";
+  static final String LRU_MULTI_PERCENTAGE_CONFIG_NAME = "hbase.lru.blockcache.multi.percentage";
+  static final String LRU_MEMORY_PERCENTAGE_CONFIG_NAME = "hbase.lru.blockcache.memory.percentage";
+
+  /**
+   * Configuration key to force data-block always(except in-memory are too much)
+   * cached in memory for in-memory hfile, unlike inMemory, which is a column-family
+   * configuration, inMemoryForceMode is a cluster-wide configuration
+   */
+  static final String LRU_IN_MEMORY_FORCE_MODE_CONFIG_NAME = "hbase.lru.rs.inmemoryforcemode";
 
   /** Default Configuration Parameters*/
 
@@ -117,6 +127,8 @@ public class LruBlockCache implements Bl
   static final float DEFAULT_MULTI_FACTOR = 0.50f;
   static final float DEFAULT_MEMORY_FACTOR = 0.25f;
 
+  static final boolean DEFAULT_IN_MEMORY_FORCE_MODE = false;
+
   /** Statistics thread */
   static final int statThreadPeriod = 60 * 5;
 
@@ -176,6 +188,9 @@ public class LruBlockCache implements Bl
   /** Overhead of the structure itself */
   private long overhead;
 
+  /** Whether in-memory hfile's data block has higher priority when evicting */
+  private boolean forceInMemory;
+
   /** Where to send victims (blocks evicted from the cache) */
   private BucketCache victimHandler = null;
 
@@ -200,8 +215,11 @@ public class LruBlockCache implements Bl
         (int)Math.ceil(1.2*maxSize/blockSize),
         DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL,
         DEFAULT_MIN_FACTOR, DEFAULT_ACCEPTABLE_FACTOR,
-        DEFAULT_SINGLE_FACTOR, DEFAULT_MULTI_FACTOR,
-        DEFAULT_MEMORY_FACTOR);
+        DEFAULT_SINGLE_FACTOR,
+        DEFAULT_MULTI_FACTOR,
+        DEFAULT_MEMORY_FACTOR,
+        false
+        );
   }
 
   public LruBlockCache(long maxSize, long blockSize, boolean evictionThread, Configuration conf) {
@@ -211,9 +229,11 @@ public class LruBlockCache implements Bl
         DEFAULT_CONCURRENCY_LEVEL,
         conf.getFloat(LRU_MIN_FACTOR_CONFIG_NAME, DEFAULT_MIN_FACTOR),
         conf.getFloat(LRU_ACCEPTABLE_FACTOR_CONFIG_NAME, DEFAULT_ACCEPTABLE_FACTOR),
-        DEFAULT_SINGLE_FACTOR,
-        DEFAULT_MULTI_FACTOR,
-        DEFAULT_MEMORY_FACTOR);
+        conf.getFloat(LRU_SINGLE_PERCENTAGE_CONFIG_NAME, DEFAULT_SINGLE_FACTOR),
+        conf.getFloat(LRU_MULTI_PERCENTAGE_CONFIG_NAME, DEFAULT_MULTI_FACTOR),
+        conf.getFloat(LRU_MEMORY_PERCENTAGE_CONFIG_NAME, DEFAULT_MEMORY_FACTOR),
+        conf.getBoolean(LRU_IN_MEMORY_FORCE_MODE_CONFIG_NAME, DEFAULT_IN_MEMORY_FORCE_MODE)
+        );
   }
 
   public LruBlockCache(long maxSize, long blockSize, Configuration conf) {
@@ -236,11 +256,12 @@ public class LruBlockCache implements Bl
    */
   public LruBlockCache(long maxSize, long blockSize, boolean evictionThread,
       int mapInitialSize, float mapLoadFactor, int mapConcurrencyLevel,
-      float minFactor, float acceptableFactor,
-      float singleFactor, float multiFactor, float memoryFactor) {
-    if(singleFactor + multiFactor + memoryFactor != 1) {
+      float minFactor, float acceptableFactor, float singleFactor,
+      float multiFactor, float memoryFactor, boolean forceInMemory) {
+    if(singleFactor + multiFactor + memoryFactor != 1 ||
+        singleFactor < 0 || multiFactor < 0 || memoryFactor < 0) {
       throw new IllegalArgumentException("Single, multi, and memory factors " +
-          " should total 1.0");
+          " should be non-negative and total 1.0");
     }
     if(minFactor >= acceptableFactor) {
       throw new IllegalArgumentException("minFactor must be smaller than acceptableFactor");
@@ -250,6 +271,7 @@ public class LruBlockCache implements Bl
     }
     this.maxSize = maxSize;
     this.blockSize = blockSize;
+    this.forceInMemory = forceInMemory;
     map = new ConcurrentHashMap<BlockCacheKey,CachedBlock>(mapInitialSize,
         mapLoadFactor, mapConcurrencyLevel);
     this.minFactor = minFactor;
@@ -497,25 +519,57 @@ public class LruBlockCache implements Bl
         }
       }
 
-      PriorityQueue<BlockBucket> bucketQueue =
-        new PriorityQueue<BlockBucket>(3);
-
-      bucketQueue.add(bucketSingle);
-      bucketQueue.add(bucketMulti);
-      bucketQueue.add(bucketMemory);
-
-      int remainingBuckets = 3;
       long bytesFreed = 0;
-
-      BlockBucket bucket;
-      while((bucket = bucketQueue.poll()) != null) {
-        long overflow = bucket.overflow();
-        if(overflow > 0) {
-          long bucketBytesToFree = Math.min(overflow,
-            (bytesToFree - bytesFreed) / remainingBuckets);
-          bytesFreed += bucket.free(bucketBytesToFree);
+      if (forceInMemory || memoryFactor > 0.999f) {
+        long s = bucketSingle.totalSize();
+        long m = bucketMulti.totalSize();
+        if (bytesToFree > (s + m)) {
+          // this means we need to evict blocks in memory bucket to make room,
+          // so the single and multi buckets will be emptied
+          bytesFreed = bucketSingle.free(s);
+          bytesFreed += bucketMulti.free(m);
+          bytesFreed += bucketMemory.free(bytesToFree - bytesFreed);
+        } else {
+          // this means no need to evict block in memory bucket,
+          // and we try best to make the ratio between single-bucket and
+          // multi-bucket is 1:2
+          long bytesRemain = s + m - bytesToFree;
+          if (3 * s <= bytesRemain) {
+            // single-bucket is small enough that no eviction happens for it
+            // hence all eviction goes from multi-bucket
+            bytesFreed = bucketMulti.free(bytesToFree);
+          } else if (3 * m <= 2 * bytesRemain) {
+            // multi-bucket is small enough that no eviction happens for it
+            // hence all eviction goes from single-bucket
+            bytesFreed = bucketSingle.free(bytesToFree);
+          } else {
+            // both buckets need to evict some blocks
+            bytesFreed = bucketSingle.free(s - bytesRemain / 3);
+            if (bytesFreed < bytesToFree) {
+              bytesFreed += bucketMulti.free(bytesToFree - bytesFreed);
+            }
+          }
+        }
+      } else {
+        PriorityQueue<BlockBucket> bucketQueue =
+          new PriorityQueue<BlockBucket>(3);
+
+        bucketQueue.add(bucketSingle);
+        bucketQueue.add(bucketMulti);
+        bucketQueue.add(bucketMemory);
+
+        int remainingBuckets = 3;
+
+        BlockBucket bucket;
+        while((bucket = bucketQueue.poll()) != null) {
+          long overflow = bucket.overflow();
+          if(overflow > 0) {
+            long bucketBytesToFree = Math.min(overflow,
+                (bytesToFree - bytesFreed) / remainingBuckets);
+            bytesFreed += bucket.free(bucketBytesToFree);
+          }
+          remainingBuckets--;
         }
-        remainingBuckets--;
       }
 
       if (LOG.isTraceEnabled()) {

Modified: hbase/branches/0.98/hbase-server/src/test/java/org/apache/hadoop/hbase/io/hfile/TestLruBlockCache.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.98/hbase-server/src/test/java/org/apache/hadoop/hbase/io/hfile/TestLruBlockCache.java?rev=1557298&r1=1557297&r2=1557298&view=diff
==============================================================================
--- hbase/branches/0.98/hbase-server/src/test/java/org/apache/hadoop/hbase/io/hfile/TestLruBlockCache.java (original)
+++ hbase/branches/0.98/hbase-server/src/test/java/org/apache/hadoop/hbase/io/hfile/TestLruBlockCache.java Sat Jan 11 01:30:02 2014
@@ -255,8 +255,8 @@ public class TestLruBlockCache {
         0.99f, // acceptable
         0.33f, // single
         0.33f, // multi
-        0.34f);// memory
-
+        0.34f, // memory
+        false);
 
     CachedItem [] singleBlocks = generateFixedBlocks(5, blockSize, "single");
     CachedItem [] multiBlocks = generateFixedBlocks(5, blockSize, "multi");
@@ -360,8 +360,109 @@ public class TestLruBlockCache {
     assertEquals(null, cache.getBlock(memoryBlocks[1].cacheKey, true, false));
     assertEquals(null, cache.getBlock(memoryBlocks[2].cacheKey, true, false));
     assertEquals(null, cache.getBlock(memoryBlocks[3].cacheKey, true, false));
+  }
+
+  @Test
+  public void testCacheEvictionInMemoryForceMode() throws Exception {
+    long maxSize = 100000;
+    long blockSize = calculateBlockSize(maxSize, 10);
 
+    LruBlockCache cache = new LruBlockCache(maxSize, blockSize, false,
+        (int)Math.ceil(1.2*maxSize/blockSize),
+        LruBlockCache.DEFAULT_LOAD_FACTOR,
+        LruBlockCache.DEFAULT_CONCURRENCY_LEVEL,
+        0.98f, // min
+        0.99f, // acceptable
+        0.2f, // single
+        0.3f, // multi
+        0.5f, // memory
+        true);
+
+    CachedItem [] singleBlocks = generateFixedBlocks(10, blockSize, "single");
+    CachedItem [] multiBlocks = generateFixedBlocks(10, blockSize, "multi");
+    CachedItem [] memoryBlocks = generateFixedBlocks(10, blockSize, "memory");
+
+    long expectedCacheSize = cache.heapSize();
+
+    // 0. Add 5 single blocks and 4 multi blocks to make cache full, si:mu:me = 5:4:0
+    for(int i = 0; i < 4; i++) {
+      // Just add single blocks
+      cache.cacheBlock(singleBlocks[i].cacheKey, singleBlocks[i]);
+      expectedCacheSize += singleBlocks[i].cacheBlockHeapSize();
+      // Add and get multi blocks
+      cache.cacheBlock(multiBlocks[i].cacheKey, multiBlocks[i]);
+      expectedCacheSize += multiBlocks[i].cacheBlockHeapSize();
+      cache.getBlock(multiBlocks[i].cacheKey, true, false);
+    }
+    // 5th single block
+    cache.cacheBlock(singleBlocks[4].cacheKey, singleBlocks[4]);
+    expectedCacheSize += singleBlocks[4].cacheBlockHeapSize();
+    // Do not expect any evictions yet
+    assertEquals(0, cache.getEvictionCount());
+    // Verify cache size
+    assertEquals(expectedCacheSize, cache.heapSize());
+
+    // 1. Insert a memory block, oldest single should be evicted, si:mu:me = 4:4:1
+    cache.cacheBlock(memoryBlocks[0].cacheKey, memoryBlocks[0], true);
+    // Single eviction, one block evicted
+    assertEquals(1, cache.getEvictionCount());
+    assertEquals(1, cache.getEvictedCount());
+    // Verify oldest single block (index = 0) is the one evicted
+    assertEquals(null, cache.getBlock(singleBlocks[0].cacheKey, true, false));
+
+    // 2. Insert another memory block, another single evicted, si:mu:me = 3:4:2
+    cache.cacheBlock(memoryBlocks[1].cacheKey, memoryBlocks[1], true);
+    // Two evictions, two evicted.
+    assertEquals(2, cache.getEvictionCount());
+    assertEquals(2, cache.getEvictedCount());
+    // Current oldest single block (index = 1) should be evicted now
+    assertEquals(null, cache.getBlock(singleBlocks[1].cacheKey, true, false));
+
+    // 3. Insert 4 memory blocks, 2 single and 2 multi evicted, si:mu:me = 1:2:6
+    cache.cacheBlock(memoryBlocks[2].cacheKey, memoryBlocks[2], true);
+    cache.cacheBlock(memoryBlocks[3].cacheKey, memoryBlocks[3], true);
+    cache.cacheBlock(memoryBlocks[4].cacheKey, memoryBlocks[4], true);
+    cache.cacheBlock(memoryBlocks[5].cacheKey, memoryBlocks[5], true);
+    // Three evictions, three evicted.
+    assertEquals(6, cache.getEvictionCount());
+    assertEquals(6, cache.getEvictedCount());
+    // two oldest single blocks and two oldest multi blocks evicted
+    assertEquals(null, cache.getBlock(singleBlocks[2].cacheKey, true, false));
+    assertEquals(null, cache.getBlock(singleBlocks[3].cacheKey, true, false));
+    assertEquals(null, cache.getBlock(multiBlocks[0].cacheKey, true, false));
+    assertEquals(null, cache.getBlock(multiBlocks[1].cacheKey, true, false));
+
+    // 4. Insert 3 memory blocks, the remaining 1 single and 2 multi evicted
+    // si:mu:me = 0:0:9
+    cache.cacheBlock(memoryBlocks[6].cacheKey, memoryBlocks[6], true);
+    cache.cacheBlock(memoryBlocks[7].cacheKey, memoryBlocks[7], true);
+    cache.cacheBlock(memoryBlocks[8].cacheKey, memoryBlocks[8], true);
+    // Three evictions, three evicted.
+    assertEquals(9, cache.getEvictionCount());
+    assertEquals(9, cache.getEvictedCount());
+    // one oldest single block and two oldest multi blocks evicted
+    assertEquals(null, cache.getBlock(singleBlocks[4].cacheKey, true, false));
+    assertEquals(null, cache.getBlock(multiBlocks[2].cacheKey, true, false));
+    assertEquals(null, cache.getBlock(multiBlocks[3].cacheKey, true, false));
+
+    // 5. Insert one memory block, the oldest memory evicted
+    // si:mu:me = 0:0:9
+    cache.cacheBlock(memoryBlocks[9].cacheKey, memoryBlocks[9], true);
+    // one eviction, one evicted.
+    assertEquals(10, cache.getEvictionCount());
+    assertEquals(10, cache.getEvictedCount());
+    // oldest memory block evicted
+    assertEquals(null, cache.getBlock(memoryBlocks[0].cacheKey, true, false));
 
+    // 6. Insert one new single block, itself evicted immediately since
+    //    all blocks in cache are memory-type which have higher priority
+    // si:mu:me = 0:0:9 (no change)
+    cache.cacheBlock(singleBlocks[9].cacheKey, singleBlocks[9]);
+    // one eviction, one evicted.
+    assertEquals(11, cache.getEvictionCount());
+    assertEquals(11, cache.getEvictedCount());
+    // the single block just cached now evicted (can't evict memory)
+    assertEquals(null, cache.getBlock(singleBlocks[9].cacheKey, true, false));
   }
 
   // test scan resistance
@@ -379,7 +480,8 @@ public class TestLruBlockCache {
         0.99f, // acceptable
         0.33f, // single
         0.33f, // multi
-        0.34f);// memory
+        0.34f, // memory
+        false);
 
     CachedItem [] singleBlocks = generateFixedBlocks(20, blockSize, "single");
     CachedItem [] multiBlocks = generateFixedBlocks(5, blockSize, "multi");
@@ -442,7 +544,8 @@ public class TestLruBlockCache {
         0.99f, // acceptable
         0.33f, // single
         0.33f, // multi
-        0.34f);// memory
+        0.34f, // memory
+        false);
 
     CachedItem [] singleBlocks = generateFixedBlocks(10, blockSize, "single");
     CachedItem [] multiBlocks = generateFixedBlocks(10, blockSize, "multi");