原文地址:Spring Transaction + MyBatis SqlSession事務管理機制研究學習html
線上的系統中,使用的是Spring+Mybatis+Mysql搭建的框架,因爲客戶須要,最近一直在對性能提高部分進行考慮,主要是涉及Mysql的一些重要參數的配置學習,以及Spring事務管理機制的學習,由於經過觀察服務器日誌,發如今這兩部分的時候耗時比較嚴重,特別是進行mysql事務提交的時候,項目源碼中使用了Spring的聲明式事務,即經過@Transactional註解來控制事務的開啓與提交,這兩天看了一些關於Spring Transaction事務的一些文章,也debug了源碼,總算有點心得和疑問,這裏簡單的整理一下。java
在Spring的配置文件中,咱們使用了"org.springframework.jdbc.datasource.DataSourceTransactionManager"對事務進行管理,翻開DataSourceTransactionManager的源碼,咱們看到DataSourceTransactionManager繼承了AbstractPlatformTransactionManager(抽象的事務管理器),DataSourceTransactionManager重寫了其中的一些方法,具體每一個方法的做用,限於篇幅,本文再也不贅述,這裏《Spring技術內幕》學習筆記16——Spring具體事務處理器的實現有詳細的介紹。mysql
在現有的項目中,咱們在public方法上面使用了@Transactional註解,當有線程調用此方法時,Spring會首先掃描到@Transactional註解,進入DataSourceTransactionManager繼承自AbstractPlatformTransactionManager的getTransaction()方法,在getTransaction()方法內部,會調用doGetTransaction()方法,@Transactional的註解中,存在一個事務傳播行爲的概念,即propagation參數,默認等於PROPAGATION_REQUIRED,表示若是當前沒有事務,就新建一個事務,若是存在一個事務,方法塊將使用這個事務,具體其餘參數的意義請看下圖:
web
在getTransaction方法中,DataSourceTransactionManager重寫了isExistingTransaction()方法,用於判斷當前是否存在事務,如下是其的源碼:spring
@Override protected boolean isExistingTransaction(Object transaction) { logger.debug(Thread.currentThread().getName() + ">>>" + "DataSourceTransactionManager.isExistingTransaction()"); DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction; return (txObject.getConnectionHolder() != null && txObject.getConnectionHolder().isTransactionActive()); }
可是,這幾天我對源碼進行調試的過程當中,發現多線程併發的時候,isExistingTransaction方法老是返回的false,即ConnectionHolder老是爲空,這是遇到的第一個疑問點,目前尚未弄清楚。因爲源碼判斷當前不存在事務,因此老是會Creating new transaction,即新建一個事務。新建事務以後,會執行重寫的doBegin()方法,在doBegin方法中,首先經過下面的代碼判斷了ConnectionHolder是否爲空,以下:sql
if (txObject.getConnectionHolder() == null || txObject.getConnectionHolder().isSynchronizedWithTransaction()) { Connection newCon = this.dataSource.getConnection(); if (logger.isDebugEnabled()) { logger.debug(Thread.currentThread().getName() + ">>>" + "Acquired Connection [" + newCon + "] for JDBC transaction"); } txObject.setConnectionHolder(new ConnectionHolder(newCon), true); }
這裏會從當前配置的數據源中獲取一個鏈接,而後設置相應的ConnectionHolder,接下來是關鍵的一步,也是存在的第二個問題點,拿到connection後,會首先判斷connection的autoCommit屬性是否爲true,以前工做中在使用原始JDBC的時候,當進行事務的控制時,咱們老是會首先設置autoCommit爲false,禁止事務自動提交,而後commit提交事務,最後設置autoCommit爲true。Spring Transaction也是這樣進行管理的,可是問題來了, 先看源碼:數據庫
if (con.getAutoCommit()) { txObject.setMustRestoreAutoCommit(true); if (logger.isDebugEnabled()) { logger.debug(Thread.currentThread().getName() + ">>>" + "Switching JDBC Connection [" + con + "] to manual commit"); } con.setAutoCommit(false); logger.debug(Thread.currentThread().getName() + ">>>" + "Set Transaction AutoCommit False [" + con + "]"); }
這裏有個Swithching JDBC Connection的操做,就是爲了設置autoCommit爲false,可是在這幾天進行多線程併發測試的時候,發現這一部分的代碼耗時很是嚴重,這是目前還不清楚的第二點。服務器
接下來,開始涉及MyBatis SqlSession部分的一些機制,關於MyBatis sqlSession的一點整理,SqlSession主要是MyBatis定義的用於進行持久化操做的對象,對connection進行了包裝。在上面Spring Transaction的機制中,咱們獲取到了connection,以後會首先調用SqlSessionUtils裏面的getSession方法,判斷當前的sqlSessionHolder以及是否存在事務鎖定。session
若是不存在sessionHolder或resources未被事務鎖定,就會Creating a new SqlSession,而後爲sqlSession註冊事務資源,即Registering transaction synchronization for SqlSession。以後把connection交給Spring管理;若是存在可用的sessionHolder而且被事務鎖定,就會從當前的事務中拿到SqlSession,即Fetched SqlSession from current transaction。當代碼中涉及數據庫的操做時,就能夠從數據源中獲取到相應的connection,即Using Connection,這時候就有了第三個問題,在進行debug源碼測試的時候,發現using connection這一步的耗時也比較嚴重。多線程
在使用完鏈接後,SqlSessionUtils會釋放事務的SqlSession,即Releasing transactional SqlSession。接下來,就能夠準備執行事務的提交了,即Initiating transaction commit,這裏會調用DataSourceTransactionManager中重寫的doCommit()方法,在其中進行事務的提交操做,即Committing JDBC transaction on Connection,爲當前的鏈接提交事務。這裏又有了第四個問題,進行事務提交的操做,在進行多線程併發測試的時候,發現耗時很是嚴重。也嘗試過修改鏈接池或者mysql的配置,問題老是得不到解決。
在事務提交以後,又回到SqlSessionUtils執行其中的afterCompletion方法,進行MyBatis SqlSession層面的處理。SqlSessionUtils會首先Transaction synchronization committing SqlSession,提交SqlSession,而後關閉SqlSession,即Transaction synchronization closing SqlSession,在MyBatis層面處理完成後,會再次回到DataSourceTransactionManager,執行其中的doCleanupAfterCompletion方法,釋放一些資源,包括: Releasing JDBC Connection 釋放JDBC鏈接,Returning JDBC Connection to DataSource 將鏈接放回到數據源。
至此,對於Spring Transaction + MyBatis SqlSession事務管理機制,已經作了大體的研究學習,閱讀了其中涉及的源碼,以及參閱了一些網上的博客,有了一些本身的認識,很近本身的理解整理出了一個簡單的時序圖,其中也有一些疑問,最後作一下記錄。
主要是包括四個疑問點:
問題1: 在現有框架下isExistingTransaction返回false,即ConnectionHolder爲空
問題2: 切換JDBC connection屬性,主要是setAutoCommit爲false,禁止事務自動提交,耗時不穩定,有時很慢
問題3: 使用數據源中的connection時,connection獲取耗時有時很慢
問題4: Spring Transaction再進行事務提交時commit耗時嚴重
這裏根據本身的理解整理出了一個簡單的時序圖,共享給你們,其中必定涉及一些不合理甚至理解錯誤的地方,但願你們不吝賜教。