0
点赞
收藏
分享

微信扫一扫

缓存设计

目录

  • 缓存设计需要考虑的地方
  • 项目代码编写
  • mybaits缓存设计原理
  • guava缓存设计原理

本地缓存设计需要考虑的地方

  1. 数据结构使用
  2. 对象上限
  3. 清除策略
  4. 过期时间
  5. 线程安全
  6. 是否持久化

本地缓存项目代码编写

  • 提供公共的泛型抽象类
public interface Cache<K, V> {
    V get(K key);

    Map<K, V> getAll(List<K> keys);

    void put(K key, V value);

    void refresh(K key);

    void refreshAll(List<K> keys);
}

public abstract class WrapLoadingCache<K, V> implements Cache<K, V>{

    private static final Logger log = LoggerFactory.getLogger(WrapLoadingCache.class);

    protected LoadingCache<K, Optional<V>> loaderCache;

    protected abstract Optional<V> load(K key);

   
    protected Map<K, Optional<V>> loadAll(List<K> keys){
        throw new UnsupportedOperationException();
    }

    protected void init(int time, TimeUnit unit, int maxSize) {

        loaderCache = CacheBuilder.newBuilder()
                .expireAfterWrite(time, unit)
                .maximumSize(maxSize)
                .build(new CacheLoader<K, Optional<V>>() {
                    @Override
                    public Optional<V> load(K key) {
                        return LoaderCache.this.load(key);
                    }

                    @Override
                    public Map<K, Optional<V>> loadAll(Iterable<? extends K> keys) throws Exception {
                        return LoaderCache.this.loadAll(Lists.newArrayList(keys));
                    }
                });
    }

    @Override
    public V get(K key) {
        Optional<V> opt = null;
        try {
            opt = loaderCache.get(key);
        } catch (ExecutionException e) {
            log.warn("WrapLoadingCacheget throw exception", e);
        }
        return opt != null && opt.isPresent() ? opt.get() : null;
    }

    @Override
    public Map<K, V> getAll(List<K> keys) {
        try {
            Map<K, V> result = new HashMap<>();
            ImmutableMap<K, Optional<V>> map = loaderCache.getAll(keys);
            map.forEach((key, opt) -> {
                V v = opt != null && opt.isPresent() ? opt.get() : null;
                result.put(key, v);
            });

            return result;
        } catch (ExecutionException e) {
            log.warn("WrapLoadingCache get throw exception", e);
        }

        return null;
    }

    @Override
    public void put(K key, V value) {
        loaderCache.put(key, Optional.ofNullable(value));
    }

    @Override
    public void refresh(K key) {
        loaderCache.refresh(key);
    }

    @Override
    public void refreshAll(List<K> keys) {
        loaderCache.putAll(loadAll(keys));
    }
}
  • 各个功能如果要使用则,继承WrapLoadingCache,并实现InitializingBean以便spring启动时调用init方法,init方法可以传入时间之类的过期设置参数。
  • load方法各自子类自个设计, 本地缓存后可跟redis缓存redis总结中redis实践有需要注意的redis缓存实践参考。

mybaits缓存设计原理

缓存图
  • 其中Cache是接口,定义了一些公共方法,PerpetualCache是一级缓存,LruCache,SynchronizedCache, BlockingCache等类都是二级缓存,采用装饰器模式,装饰一级缓存。一级缓存是SqlSession级别的,二级缓存是Mapping级别的存在线程安全问题,当然mybatis提供线程安全的Cache,但是mybatis处理二级缓存虽然解决了线程安全问题,并没有提供RR的隔离级别,只提供RC。


LruCache
  • LruCache 的 keyMap 属性是实现 LRU 策略的关键,该属性类型继承自 LinkedHashMap,并覆盖了 removeEldestEntry 方法。LinkedHashMap 内部的 tail 节点会指向最新插入的节点。head 节点则指向第一个被插入的键值对,也就是最久未被访问的那个键值对。默认情况下,LinkedHashMap 仅维护键值对的插入顺序。LinkedHashMap具体实现Lru细节可参考LinkedHashMap 源码详细分析
BlockingCache
  • getObject 方法会先获取与 key 对应的锁,并加锁。若缓存命中,getObject 方法会释放锁,否则将一直锁定。getObject 方法若返回 null,表示缓存未命中。
  • 使用分段锁的思想,一个key对应一个ReentrantLock
private final ConcurrentHashMap<Object, ReentrantLock> locks;
mybatis二级缓存导致的不可重复读问题

guava缓存设计原理

// CacheLoader形式的Cache
 private static final LoadingCache<String, String> LOADER_CACHE = CacheBuilder.newBuilder()
            .expireAfterAccess(1, TimeUnit.SECONDS)
            .maximumSize(1000)
            .build(new CacheLoader<String, String>() {
                @Override
                public String load(String key) throws Exception {
                    return key + new Date();
                }
            });

参考文章

举报

相关推荐

0 条评论