緩存設(shè)計

目錄

  • 緩存設(shè)計需要考慮的地方
  • 項目代碼編寫
  • mybaits緩存設(shè)計原理
  • guava緩存設(shè)計原理

本地緩存設(shè)計需要考慮的地方

  1. 數(shù)據(jù)結(jié)構(gòu)使用
  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,并實現(xiàn)InitializingBean以便spring啟動時調(diào)用init方法,init方法可以傳入時間之類的過期設(shè)置參數(shù)。
  • load方法各自子類自個設(shè)計, 本地緩存后可跟redis緩存redis總結(jié)中redis實踐有需要注意的redis緩存實踐參考。

mybaits緩存設(shè)計原理

緩存圖
  • 其中Cache是接口,定義了一些公共方法,PerpetualCache是一級緩存,LruCache,SynchronizedCache, BlockingCache等類都是二級緩存,采用裝飾器模式,裝飾一級緩存。一級緩存是SqlSession級別的,二級緩存是Mapping級別的存在線程安全問題,當(dāng)然mybatis提供線程安全的Cache,但是mybatis處理二級緩存雖然解決了線程安全問題,并沒有提供RR的隔離級別,只提供RC。


    緩存.png
LruCache
  • LruCache 的 keyMap 屬性是實現(xiàn) LRU 策略的關(guān)鍵,該屬性類型繼承自 LinkedHashMap,并覆蓋了 removeEldestEntry 方法。LinkedHashMap 內(nèi)部的 tail 節(jié)點會指向最新插入的節(jié)點。head 節(jié)點則指向第一個被插入的鍵值對,也就是最久未被訪問的那個鍵值對。默認(rèn)情況下,LinkedHashMap 僅維護(hù)鍵值對的插入順序。LinkedHashMap具體實現(xiàn)Lru細(xì)節(jié)可參考LinkedHashMap 源碼詳細(xì)分析
BlockingCache
  • getObject 方法會先獲取與 key 對應(yīng)的鎖,并加鎖。若緩存命中,getObject 方法會釋放鎖,否則將一直鎖定。getObject 方法若返回 null,表示緩存未命中。
  • 使用分段鎖的思想,一個key對應(yīng)一個ReentrantLock
private final ConcurrentHashMap<Object, ReentrantLock> locks;
mybatis二級緩存導(dǎo)致的不可重復(fù)讀問題

guava緩存設(shè)計原理

  • 并發(fā)方面的實現(xiàn)類似ConcurrentHashMap1.7的分段鎖(分段Segment),LoadingCache寫法的實現(xiàn)在get時無數(shù)據(jù)會自動調(diào)用load方法。LoadingCache支持自動刷新。具體細(xì)節(jié)可參考Guava LocalCache 緩存介紹及實現(xiàn)源碼深入剖析
// 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();
                }
            });

參考文章

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容