天天看点

HBase-1.2.4LruBlockCache实现分析(一)

一、简介

      BlockCache是HBase中的一个重要特性,相比于写数据时缓存为Memstore,读数据时的缓存则为BlockCache。

      LruBlockCache是HBase中BlockCache的默认实现,它采用严格的LRU算法来淘汰Block。

二、缓存级别

      目前有三种缓存级别,定义在BlockPriority中,如下:

      1、SINGLE:主要用于scan等,避免大量的这种一次的访问导致缓存替换;

      2、MULTI:多次缓存;

      3、MEMORY:常驻缓存的,比如meta信息等。

三、缓存实现分析

      LruBlockCache缓存的实现在方法cacheBlock()中,实现逻辑如下:

      1、首先需要判断需要缓存的数据大小是否超过最大块大小,按照2%的频率记录warn类型log并返回;

      2、从缓存map中根据cacheKey尝试获取已缓存数据块cb;

      3、如果已经缓存过,比对下内容,如果内容不一样,抛出异常,否则记录warn类型log并返回;

      4、获取当前缓存大小currentSize,获取可以接受的缓存大小currentAcceptableSize,计算硬性限制大小hardLimitSize;

      5、如果当前大小超过硬性限制,当回收没在执行时,执行回收并返回,否则直接返回;

      6、利用cacheKey、数据buf等构造Lru缓存数据块实例cb;

      7、将cb放置入map缓存中;

      8、元素个数原子性增1;

      9、如果新大小超过当前可以接受的大小,且未执行回收过程中,执行内存回收。

      详细代码如下,可自行阅读分析:

四、淘汰缓存实现分析

      淘汰缓存的实现方式有两种:

      1、第一种是在主线程中执行缓存淘汰;

      2、第二种是在一个专门的淘汰线程中通过持有对外部类LruBlockCache的弱引用WeakReference来执行缓存淘汰。

      应用那种方式,取决于构造函数中的boolean参数evictionThread,如下:

      而在执行淘汰缓存的runEviction()方法中,有如下判断:

        而EvictionThread的evict()实现如下:

        通过synchronized获取EvictionThread线程的对象锁,然后主线程通过回收线程对象的notifyAll唤醒EvictionThread线程,那么这个线程是何时wait的呢?答案就在其run()方法中,notifyAll()之后,线程run()方法得以继续执行:

        线程会wait10s,放弃对象锁,在notifyAll()后,继续执行后面的淘汰流程,即:

        逻辑比较清晰,如下:

        1、通过可重入互斥锁ReentrantLock确保同一时刻只有一个回收在执行;

        2、设置标志位evictionInProgress,是否正在进行回收过程为true;

        3、获取当前缓存大小currentSize;

        4、计算应该释放的缓冲大小bytesToFree:currentSize - minSize();

        5、如果需要回收的大小小于等于0,直接返回;

        6、实例化优先级队列:single、multi、memory;

        7、扫描缓存,分别加入上述三个优先级队列;

        8、如果forceInMemory或者InMemory缓存超过99.9%:

              8.1、如果需要回收的缓存超过则全部回收Single、Multi中的缓存大小和,则全部回收Single、Multi中的缓存,剩余的则从InMemory中回收(this means we need to evict blocks in memory bucket to make room,so the single and multi buckets will be emptied):

              8.2、否则,不需要从InMemory中回收,按照如下策略回收Single、Multi中的缓存:尝试让single-bucket和multi-bucket的比例为1:2:

                        8.2.1、 single-bucket足够小,从multi-bucket中回收;

                        8.2.2、 multi-bucket足够小,从single-bucket中回收;

                        8.2.3、single-bucket和multi-bucket中都回收,且尽量满足回收后比例为1:2;

        9、否则,从三个队列中循环回收;

        10、最后,重置标志位,释放锁等。

四、实例化

        参见《HBase-1.2.4 Allow block cache to be external分析》最后。

继续阅读