亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何理解Mybatis源碼中的Cache

發布時間:2021-09-14 10:36:08 來源:億速云 閱讀:108 作者:柒染 欄目:大數據

這篇文章將為大家詳細講解有關如何理解Mybatis源碼中的Cache,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。

緩存的使用場景

  • 通過復雜業務計算得來的數據,在計算過程中可能耗費大量的時間,需要將數據緩存

  • 讀多寫少的數據

緩存設計的要點

  • 緩存的容量

  • 緩存的有效時間

實踐中可能存在的問題

緩存穿透

訪問的緩存不存在,直接去訪問數據庫。通常查找的key沒有對應的緩存,可以設計為返回空值,不去查找數據庫。

緩存雪崩

大量的緩存穿透會導致有大量請求,訪問都會落到數據庫上,造成緩存雪崩。所以如果訪問的key在緩存中找不到,不要直接去查詢數據庫,也就是要避免緩存穿透,可以設置緩存為永久緩存,然后通過后臺定時更新緩存。也可以為緩存更新添加鎖保護,確保當前只有一個線程更新數據。

Mybatis中的緩存分析與學習

MyBatis的一級緩存和二級緩存

一級緩存

  • 使用范圍:SESSION,STATEMENT。默認為SESSION,如果不想使用一級緩存,可以修改為STATEMENT,每次調用后都會清掉緩存。

//STATEMENT或者SESSION,默認為SESSION
<setting name="localCacheScope" value="STATEMENT"/>
  • 一級緩存執行的流程: 如何理解Mybatis源碼中的Cache

//BaseExecutor.query
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
   ...
    List<E> list;
    try {
      ...
      //先根據cachekey從localCache去查
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
      if (list != null) {
        //若查到localCache緩存,處理localOutputParameterCache
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
        //從數據庫查
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      //清空堆棧
      queryStack--;
    }
    ...
    if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        // issue #482
    	//如果是STATEMENT,清本地緩存
        clearLocalCache();
      }
    return list;
  }
  • 什么情況下有效?在,一個sqlSession打開和關閉的范圍內,所以,如果被Spring托管并且沒有開啟事務,那么一級緩存是失效的。

  • 注意: 打開兩個sqlsession: sqlsession1,sqlsession2,sqlsession1.select從一級緩存中取數據,sqlsession2更新此條數據,sqlsession1.select再次獲取數據,還是緩存中的。

二級緩存

  • SessionFactory層面給各個SqlSession 對象共享

  • 怎么配置:

<setting name="cacheEnabled" value="true" /> (或@CacheNamespace) 
<cache/>,<cache-ref/>或@CacheNamespace
//每個select設置
  <select id="queryDynamicFlightVOs" resultType="com.ytkj.aoms.aiis.flightschedule.vo.DynamicFlightVO" useCache="false">

如果配置了二級緩存,那么在獲取Executor的時候會返回CachingExecutor

//Configuration.java
  //產生執行器
  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    //這句再做一下保護,,防止粗心大意的人將defaultExecutorType設成null?
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    //然后就是簡單的3個分支,產生3種執行器BatchExecutor/ReuseExecutor/SimpleExecutor
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
    //如果要求緩存,生成另一種CachingExecutor(默認就是有緩存),裝飾者模式,所以默認都是返回CachingExecutor
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    //此處調用插件,通過插件可以改變Executor行為
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

開啟了二級緩存后,會先使用CachingExecutor查詢,查詢不到在查一級緩存。 如何理解Mybatis源碼中的Cache

   public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        //namespace配置作用
        Cache cache = ms.getCache();
        if (cache != null) {
            //是否執行刷新緩存操作
            this.flushCacheIfRequired(ms);
            //useCache標簽作用
            if (ms.isUseCache() && resultHandler == null) {
                this.ensureNoOutParams(ms, parameterObject, boundSql);
                List<E> list = (List)this.tcm.getObject(cache, key);
                if (list == null) {
                    list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
                    this.tcm.putObject(cache, key, list);
                }

                return list;
            }
        }

        return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
    }

注意

  • 由于二級緩存中的數據是基于namespace的,即不同namespace中的數據互不干擾。在多個namespace中若均存在對同一個表的操作,那么這多個namespace中的數據可能就會出現不一致現象

  • 在分布式環境下,由于默認的MyBatis Cache實現都是基于本地的,分布式環境下必然會出現讀取到臟數據,需要使用集中式緩存將MyBatis的Cache接口實現,有一定的開發成本,直接使用Redis、Memcached等分布式緩存可能成本更低,安全性也更高

裝飾器模式

通過裝飾器模式,我們可以向一個現有的對象添加新的功能,同時不改變其結構。
Mybatis中不同類型的緩存,正是使用了此類設計。

PerpetualCache實現了Cache接口,完成了基本的Cache功能,其他的裝飾類對其進行功能擴展。以LruCache為例:

public class LruCache implements Cache {

  //持有實現類
  private final Cache delegate;
  //額外用了一個map才做lru,但是委托的Cache里面其實也是一個map,這樣等于用2倍的內存實現lru功能
  private Map<Object, Object> keyMap;
  private Object eldestKey;

  public LruCache(Cache delegate) {
    this.delegate = delegate;
    setSize(1024);
  }
 
    .....
}
LruCache的具體實現 [Least Recently Used]

LruCache使用了一個LinkedHashMap作為keyMap,最多存1024個key的值,超過之后新加對象到緩存時,會將不經常使用的key從keyMap中移除,并且刪除掉緩存中對應的key。利用了LinkedHashMap的特性:

  • 每次訪問或者插入一個元素都會把元素放到鏈表末尾

//LinkedHashMap的get方法
  public V get(Object key) {
        Node<K,V> e;
        if ((e = getNode(hash(key), key)) == null)
            return null;
        if (accessOrder)
            afterNodeAccess(e);
        return e.value;
    }
  • 調用put,putAll方法的時候會調用removeEldestEntry方法,所以LruCache在new LinkedHashMap的時候重寫了removeEldestEntry方法

  public void setSize(final int size) {
    keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {
      private static final long serialVersionUID = 4267176411845948333L;

      //核心就是覆蓋 LinkedHashMap.removeEldestEntry方法,
      //返回true或false告訴 LinkedHashMap要不要刪除此最老鍵值
      //LinkedHashMap內部其實就是每次訪問或者插入一個元素都會把元素放到鏈表末尾,
      //這樣不經常訪問的鍵值肯定就在鏈表開頭啦
      @Override
      protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
        boolean tooBig = size() > size;
        if (tooBig) {
            //根據eldestKey去緩存中刪除
          eldestKey = eldest.getKey();
        }
        return tooBig;
      }
    };
  }
  
   private void cycleKeyList(Object key) {
    keyMap.put(key, key);
    //keyMap是linkedhashmap,最老的記錄已經被移除了,然后這里我們還需要移除被委托的那個cache的記錄
    if (eldestKey != null) {
      delegate.removeObject(eldestKey);
      eldestKey = null;
    }
  }
Mybatis Cache類型與特性
  • BlockingCache:阻塞版本的緩存裝飾器,它保證只有一個線程到數據庫中查找指定key對應的數據, 加入線程A 在BlockingCache 中未查找到keyA對應的緩存項時,線程A會獲取keyA對應的鎖,這樣后續線程在查找keyA是會發生阻塞。

  • FifoCache:先進先出緩存,FifoCache在putObject的時候會將Key放到一個List中,list長度為1024,如果超過,則刪除第一個緩存,最后一位添加當前緩存

  • LoggingCache:日志緩存,它持有日志接口,根據日志的設置可打印sql和命中率等。

  • LruCache:最近最少使用算法。核心思想是當緩存滿時,會優先淘汰那些近期最少使用的緩存。

  • ScheduledCache:定時調度緩存,每次訪問和添加緩存的時候會判斷當前時間和上次清理的時間是否超過閾值,超過則會清理緩存。

  • SerializedCache:序列化緩存。緩存的值會被序列化。取值的時候會反序列化。要求緩存的對象實現Serializable接口。

  • SoftCache:軟引用緩存。對于軟引用關聯著的對象,只有在內存不足的時候JVM才會回收該對象,使用SoftReference可以避免OOM異常

  • WeakCache:弱引用緩存,類似SoftCache。與軟引用區別在于當JVM進行垃圾回收時,無論內存是否充足,都會回收被弱引用關聯的對象。

  • TransactionalCache:事務緩存

關于如何理解Mybatis源碼中的Cache就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

道孚县| 井冈山市| 惠安县| 宕昌县| 库尔勒市| 江安县| 工布江达县| 揭西县| 延津县| 西畴县| 二连浩特市| 策勒县| 永清县| 海林市| 葫芦岛市| 拉萨市| 安泽县| 南平市| 肥西县| 平顶山市| 长顺县| 浪卡子县| 康马县| 宣恩县| 临沭县| 扎鲁特旗| 海林市| 琼中| 石嘴山市| 开江县| 上虞市| 斗六市| 宿州市| 三河市| 巩义市| 九龙城区| 寿光市| 柘荣县| 资兴市| 容城县| 阳谷县|