Mybatis 核心包-Cache包

核心类: Cache Interface、 衍生包装类、直接子类:PrepetualCache

Mybatis 缓存键需要满足的几个特点

  • 无碰撞
  • 高效比较
  • 高效生成

为了解决上述问题,Mybatis设计了CacheKey 类作为缓存键。

CacheKey

    
    // 乘数, 用来计算hashcode 时使用
    private final int multiplier;
    // 哈希值,整个CacheKey 的哈希值。如果两个CacheKey 的该值不同,则两个CacheKey一定不同
    private int hashcode;
    // 求和校验值,整个CacheKey 的求和校验值。如果两个CacheKey 的该值不同,则两个CacheKey 一定不同
    private long checksum;
    // 更新次数,整个CacheKey 的更新次数
    private int count;
    // 更新历史
    private List<Object> updateList;

    /**
     * 更新CacheKey
     */
    public void update(Object object) {
        int baeHashCode = object == null? 1: ArrayUtil.hashCode(object);
        count++;
        checksum += baseHashCode;
        baseHashCode *= count;

        hashCode = multiplier * hashCode + baseHashCode;
        updateList.add(object);
    }

  
  public boolean equals(Object object) {
    // 如果地址一样,是一个对象,则肯定相等
    if(this == object) {
        return true;
    }

    if(!object instanceof CacheKey) {
        return false;
    }

    final CacheKey cacheKey = (CacheKey)object;

    // 依次通过hashcode、checksum、count 判断, 必须完全一致才相等
    if(hashcode != cachKey.hashcode) {
        return false;
    }
    if(checksum != cachekey.checksum) {
        return false;
    }
    if(count != cachekey.count) {
        return false;
    }
    // 比较updateList中的每次变更
    for(int i = 0; i < updateList.size(); i++) {
        Object thisObject = updatelist.get(i);
        Object thatObject = cacheKey.updateList.get(i);
        if(!ArrayUtil.equals(thisObject, thatObject)) {
            return false;
        }
    }
    return true;
  }


CacheKey 的使用

在数据库中查询会根据当前的查询条件生成一个CacheKey, 在BaseExecutor中可以看到

public <E> lsit<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameter);
    // 生成缓存的键
    CacheKey cacheKey = createCacheKey(ms, parameter, rowBounds, boundSql);
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}

Cache 的实现类

  • PerpetualCache:

    主要属性:

      // 缓存id, 一般使用映射文件的namespace作为缓存的id
      private final String id;
      // 缓存数据
      private Map<Object, Object> cache = new HashMap<Object, Object>();
    

Cache 的装饰类

根据装饰器的功能可以将他们分为以下几个大类:

  • 同步装饰器

    SynchronizedCache 类

      每个操作方法都增加了synchronized关键字    
    
  • 日志装饰器

    作用: 为缓存增加日志统计的功能,需要统计的指标包含: 命中率

    LoggingCache 类

      public Object getObject(Object key) {
          // 请求缓存次数 + 1
          request++;
          final Object value = delegate.getObject(key);
          if(value != null) {
              // 命中缓存次数 + 1
              hits++;
          }
          if(log.isDebugEnabled()) {
              log.debug("Cache Hit Ratio [" + getId() + "]:" + getHitRatio());
          }
          return value;
      }
    
  • 清理装饰器

    作用: 缓存空间有限,这是一种以空间换时间的方式,需要为缓存增加合适的清理策略

    • FifoCache 装饰器

    采用先进先出的策略来清理缓存,它内部使用了keyList属性存储了缓存数据的写入顺序,并且使用size属性存储了缓存数据的数量限制

      
      // 被装饰对象 
      private final Cache delegate;
      // 按照写入顺序保存了缓存数据的键
      private final Deque<Object> keyListl;
      // 缓存空间的大小
      private int size;
    
      pubblic void putObject(Object key, Object value) {
          cycleKeyList(key);
          delegate.putObject(key, value);
      }
    
      private void cycleKeyList(Object key) {
          keyList.addLast(key);
          if(keyList.size() > size) {
              Object oldestKey = keyList.removeFirst();
              delegate.removeObject(oldestKey);
          }
      }
    
    • LruCache 装饰器

    采用近期最少使用算法,该算法会在缓存数据数量达到设置的上限时将近期未使用的数据删除。

          // 被装饰的对象
          private final Cache delegate;
          // 使用LinkedHashMap 保存的缓存数据的键
          private Map<Object, Object> keyMap;
          // 最近最少使用的数据的键
          private Object eldestKey
    
          public LruCache(Cache delegate) {
              this.delegate = delegate;
              setSize(1024);
          }
    
          public void setSize(final int size) {
              keyMap = new LinkedHashMap<Object, Object>(size, 0.75f, true) {
                  private static final long serialVersionUID = 4267176411845948333L;
    
                  @Override
                  protected boolean removeEldestEntryy(Map.Entry<Object, Object> eldest) {
                      boolean tooBig = size() > size;
                      if(tooBig) {
                          eldestKey  = eldest.getKey();
                      }
                      return tooBig;
                  }
              }
          }
    
        @Override
        public voiid putObject(Object key, Object value) {
            delegate.putObject(key, value);
            cycleKeyList(key);
        }

        @Override
        public Object getObject(Object key) {\
            // 触及下当前被访问的键,表明它被访问了
            keyMap.get(key);
            return delegate.getObject(key);
        }


        public void cyclKeyList(Object key) {
            keyMap.put(key, key);
            if(eldestKey  != null) {
                delegate.removeObject(eldestKey);
                eldestKey = null;
            }
        }

- WeakCache 装饰器

        // 强引用的对象列表
        private final Deque<Object> hard;
        // 弱引用的对象列表
        private final ReferenceQueue<Object> queueOfGarbageCollectedEntries;
        // 被装饰对象
        private final Cache delegate;
        // 强引用对象的数目限制
        private int numberOfHardLinks;

        @Override
        public void putObject(Object key, Object value) {
            // 清除u垃圾回收队列中的元素
            removeGarbageCollectedItems();
            // 向被装饰对象中放入的值是弱引用的句柄
            delegate.put(key, new WeakEntry(key, value, queueOfGarbageCollectedEntries));
        }

        private void removeGarbageCollectedItems() {
            WeakEntry sv;
            while((sv = (WeakEntry)queueOfGarbageCollectedEntries.poll()) != null) {
                // 轮询该垃圾回收队列
                delegate.removeObject(sv.getKey())
            }
        }

        @Override
        public Object getObject(Object key) {
            Object result = nul;
            // 假定被装饰对象只被该装饰器完全控制
            WeakReference<Object> weakReference = (WeakReference<Object>) delegate.getObject(key);

            if(weakReference != null) {
                // 取到了弱引用的句柄
                // 读取弱引用的句柄
                result = weakReference.get();
                if(result == null) {
                    // 直接删除该缓存
                    delegate.removeobject(key);
                } else {
                    // 弱引用的对象还在
                    // 将缓存的数据写入强引用列表中,防止被清理
                    hardLinksToAvoidGarbageCollection.addFirst(result);
                    if(hardLinksToAvoidGarbageCollection.size() > numberOfHardLinks) {
                        // 强引用的对象数据超出限制
                        // 从强引用的列表中删除该数据
                        hadrLinksToAvoidGarbageCollection.removeLast();
                    }
                }
            }
        }

- SoftCache 装饰器


  • 阻塞装饰器

    • BlockingCache

       private long timeout;
       private final Cache delegate;
       private final ConcurrentHashMap<Object, ReentranLock> locks;
      
       private ReentranLock getLoockForKey(Object key) {
           return locks.computeIfAbsent(key, k-> new ReentrantLock());
       }
      
       private voiid acquireLock(Object key) {
           // 找出指定对象的锁
           Lock lock = getLockForKey(key);
           if(timeout > 0) {
               try {
                   boolean acquired = lock.tryLock(timeout, TimeUnit.MILLISECONDS);
                   if(!acquired) {
                       throw new CacheException("Could not acquire lock [" + lock + "] for key [" + key + "] within " + timeout + "ms");
                   }
               }catch(InterruptedException e) {
                   throw new CacheException("Got interrupted while trying to acquire lock [" + lock + "] for key [" + key + "]", e)
               }
           } else {
               lock.lock();
           }
       }
      
       private void releaseLock(Object key) {
           ReentrantLock lock = locks.get(key);
           if(lock.isHeldByCurrentThread()) {
               lock.unlock();
           }
       }
      
  • 定时清理装饰器

    • ScheduledCache 装饰器类

        private final Cache delegate;
        protected long clearInterval;
        protected long lastClear;
      
        private boolean clearWhenStale() {
            if(System.currentTimeMillis() - lastClear > clearInterval) {
                clear();
                return true;
            }
            return false;
        }
  • 序列化装饰器

    对象被放入缓存后,如果被多次取出,多次读取的是同一个对象的引用。缓存中的对象是在多个引用之间共享的。意味着引用修改对象会互相影响

    在使用SerializedCache 后,每次向缓存中写入对象时,实际写入的是对象的序列化串;每次读取对象,都会进行序列化反序列化之后再返回。通过序列化、反序列化保证了每一次缓存给出的对象都是一个对象

    @Override
    public void putObject(Object key, Object value) {
        if(object == null || object instancof Serializable) {
            // 要缓存的数据必须是可序列化的
            // 将输序列化后写入缓存
            delegate.putObject(key, serialize((Serializable)object));
        } else {
            // 要缓存的数据不可序列化
            throw new CacheException("SharedCache failed to make a copy  of a non-serializable object: " + object);
        }
    }

    @Override
    public Object getObject(Object key) {
        Object object = delegate.getObject(key);
        return object == null ? null : deserialize(([]byte) object);
    }
  • 事务装饰器

    • TransactionCache

        // 被装饰的对象
        private final Cache delegate;
        // 事务提交后是否直接清理缓存
        private boolean clearOnCommit;
        // 事务提交时需要写入的缓存数据
        private final Map<Object, Object> entriesToAddOnCommit;
        // 缓存查询未命中的数据
        private final Set<Object> entriesMissedInCache;
      
        @Override
        public Object getObject(Object key) {
            // 从缓存中读取对应的数据
            Object object = delegate.getObject(key);
            if(object == null) {
                entriesMissedInCache.add(key);
            }
            if(clearOnCommit) {
                return null;
            } else {
                return object;
            }
        }

        @Override
        public void putObject(Object key, Object object) {
            // 先放到entriesToAddCommit 列表中暂存
            entriesToAddOnCommit.put(key, object);
        }

CacheBuilder

public Cache build() {
    // 设置缓存的默认实现,默认装饰器
    setDefaultImplementations();
    Cache cache = new BaseCacheInstance(implementation,id);
    setCacheProperties(cache);
    if(PrepetualCache.class.equals(cache.getClass())) {
        for(Class<? extends Cache> decorator: decorators) {
            cache = newCacheDecoratorInstance(decorator, cache);
            setCacheProperties(cache);
        }
        cache = setStandardDecorators(cache);
    } else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {
        cache = new LoggingCache(cache);
    }
    return cache;
}

private Cache setStandardDecorators(Cache cache) {
    try {
        MetaObject metaCache = SystemMetaObject.forObject(cache);
        // 设置缓存大小
        if(size != null && metaCache.hasSetter("size")) {
            metaCache.setValue("size", size);
        }

        // 如果定义了清理间隔,则使用定时清理装饰器装饰缓存
        if(clearInterval != null) {
            cache = new ScheduledCache(cache);
            ((ScheduledCache) cache).setClearInterval(clearInterval);
        }

        // 如果允许写
        if(readWrite) {
            cache = new SerializedCache(cache);
        }

        // 使用日志装饰器装饰缓存
        cache = new LoggingCache(cache);

        // 使用同步装饰器装饰缓存
        cache = new SynchronizedCache(cache);
        // 如果启用了阻塞功能,则使用阻塞装饰器装饰缓存
        if(blocking) {
            cache = new BlockingCache(cache);
        }

        // 返回被层层装饰的缓存
        return cache;
    }catch(Exception e) {
        throw new CacheException("Error building cache decorators. Cause: "  + e, e);
    }
}

叮咚_打地鼠
0 声望0 粉丝

docker docker docker ing