mybatis中的執行器

mybatis在執行期間,主要有四大核心接口對象:

執行器Executor,執行器負責整個SQL執行過程的總體控制。
參數處理器ParameterHandler,參數處理器負責PreparedStatement入參的具體設置。
語句處理器StatementHandler,語句處理器負責和JDBC層具體交互,包括prepare語句,執行語句,以及調用ParameterHandler.parameterize()設置參數。
結果集處理器ResultSetHandler,結果處理器負責將JDBC查詢結果映射到java對象。
執行器Executor
  什麼是執行器?所有我們在應用層通過sqlSession執行的各類selectXXX和增刪改操作在做了動態sql和參數相關的封裝處理後,都被委託給具體的執行器去執行,包括一、二級緩存的管理,事務的具體管理,Statement和具體JDBC層面優化的實現等等。所以執行器比較像是sqlSession下的各個策略工廠實現,用戶通過配置決定使用哪個策略工廠。只不過執行器在一個mybatis配置下只有一個,這可能無法適應於所有的情況,尤其是哪些微服務做得不是特別好的中小型公司,因爲這些系統通常混搭了OLTP和ETL功能。

mybatis提供了下列類型的執行器:

從上述可以看出,mybatis提供了兩種類型的執行器,緩存執行器與非緩存執行器(使用哪個執行器是通過配置文件中settings下的屬性defaultExecutorType控制的,默認是SIMPLE),是否使用緩存執行器則是通過執行cacheEnabled控制的,默認是true。
  緩存執行器不是真正功能上獨立的執行器,而是非緩存執行器的裝飾器模式。
  我們先來看非緩存執行器。非緩存執行器又分爲三種,這三種類型的執行器都基於基礎執行器BaseExecutor,基礎執行器完成了大部分的公共功能,如下所示:

我們先來看下BaseExecutor的屬性,從上述BaseExecutor的定義可以看出:

執行器在特定的事務上下文下執行;
具有本地緩存和本地出參緩存(任何時候,只要事務提交或者回滾或者執行update或者查詢時設定了刷新緩存,都會清空本地緩存和本地出參緩存);
具有延遲加載任務;
  BaseExecutor實現了大部分通用功能本地緩存管理、事務提交、回滾、超時設置、延遲加載等,但是將下列4個方法留給了具體的子類實現:

從功能上來說,這三種執行器的差別在於:

ExecutorType.SIMPLE:這個執行器類型不做特殊的事情。它爲每個語句的每次執行創建一個新的預處理語句。
ExecutorType.REUSE:這個執行器類型會複用預處理語句。
ExecutorType.BATCH:這個執行器會批量執行所有更新語句,也就是jdbc addBatch API的facade模式。
  所以這三種類型的執行器可以說時應用於不同的負載場景下,除了SIMPLE類型外,另外兩種要求對系統有較好的架構設計,當然也提供了更多的回報。
 

SIMPLE執行器

       簡單執行器的實現非常的簡單,我們就不展開詳述了。

REUSE執行器

       從實現可以看出,REUSE和SIMPLE在doUpdate/doQuery上有個差別,不再是每執行一個語句就close掉了,而是儘可能的根據SQL文本進行緩存並重用,但是由於數據庫服務器端通常對每個連接以及全局的語句(oracle稱爲遊標)handler的數量有限制,oracle中是open_cursors參數控制,mysql中是mysql_stmt_close參數控制,這就會導致如果sql都是靠if各種拼接出來,日積月累可能會導致數據庫資源耗盡。其是否有足夠價值,視創建Statement語句消耗的資源佔整體資源的比例、以及一共有多少完全不同的Statement數量而定,一般來說,純粹的OLTP且非自動生成的sqlmap,它會比SIMPLE執行器更好。

 BATCH執行器

        批量執行器是JDBC Statement.addBatch的實現,對於批量insert而言比如導入大量數據的ETL,驅動器如果支持的話,能夠大幅度的提高DML語句的性能(首先最重要的是,網絡交互就大幅度減少了),比如對於mysql而言,在5.1.13以上版本的驅動,在連接字符串上rewriteBatchedStatements參數也就是jdbc:mysql://192.168.1.100:3306/test?rewriteBatchedStatements=true後,性能可以提高几十倍,

參見 https://www.cnblogs.com/kxdblog/p/4056010.html 以及 http://blog.sina.com.cn/s/blog_68b4c68f01013yog.html 。

因爲BatchExecutor對於每個statementList中的語句,都執行executeBatch()方法,因此最差的極端情況是交叉執行不同的DML SQL語句,這種情況退化爲原始的方式。比如下列形式就是最差的情況:

for(int i=0;i<100;i++) {
        session.update("insertUser", userReq);
        session.update("insertUserProfile", userReq);
    }
緩存執行器CachingExecutor

        緩存執行器相對於其他執行器的差別在於,首先是在query()方法中判斷是否使用二級緩存(也就是mapper級別的緩存)。雖然mybatis默認啓用了CachingExecutor,但是如果在mapper層面沒有明確設置二級緩存的話,就退化爲SimpleExecutor了。二級緩存的維護由TransactionalCache(事務化緩存)負責,當在TransactionalCacheManager(事務化緩存管理器)中調用putObject和removeObject方法的時候並不是馬上就把對象存放到緩存或者從緩存中刪除,而是先把這個對象放到entriesToAddOnCommit和entriesToRemoveOnCommit這兩個HashMap之中的一個裏,然後當執行commit/rollback方法時再真正地把對象存放到緩存或者從緩存中刪除,具體可以參見TransactionalCache.commit/rollback方法。
  還有一個差別是使用了TransactionalCacheManager管理事務,其他邏輯就一樣了。
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章