1. 程式人生 > >事務隔離級別引發的"血案"

事務隔離級別引發的"血案"

地方 隱藏 命令 遠程服務 平時 通過 art nbsp 原因

事務引發的"血案"見的多了也麻木了,這回遇到個事務隔離級別的"案子",坑了我小半天的時間...也怪自己細節不牢..

敲著代碼遇到這麽一個怪事情:

class XXXService{
     @Transactional
    public void demo(){
          //一堆業務邏輯
           rpc.insertOne();   //dubbo調用遠程服務插入一條數據
           getOne();   //獲取剛才插入的數據
    
    }
  
}

其中getOne()的事務的傳播屬性是required, 因為dubbo是遠程調用,所以實際上返回後插入的數據就已經commit了, 一個事務中commit的數據另一個事務中應該可以讀取到...本來是這樣的,但是呢getOne()方法中卻讀取不到新插入的數據..

倘使如果不是用dubbo遠程調用插入數據的話, 我可能還不會誤入歧途, 因為數據庫是本地自己搭建的, 平時也沒人改默認配置, 按照以往經驗Mysql默認配置的情況上面的例子應該是可以讀取到的. 所以我把關註點放到這個rpc調用上去了..浪費了很多時間..反正是折騰了很久, 各種推導測試事務傳播,想來想去也應該沒問題.後來幹脆把事務去掉了,發現正常了,傳播屬性各種測試基本確定沒有任何問題了,那麽問題可以確定是在事務隔離性上..饒了一大圈這才剛剛走上正道....

SELECT @@session.tx_isolation;

通過命令查詢了一下mysql數據庫的隔離配置,發現是 Repeatable Read,

咦, 這個和記憶中的默認配置貌似對不上(這裏實際上是我記混了,mysql默認配置的確是這個,Oracle才是Read committed)..趕緊查詢一下線上的數據庫配置果然不對應該是Read Committed

這下問題明了,由於代碼中沒有寫明隔離級別,所以使用的是mysql配置的隔離級別,而mysql的隔離級別是可重復讀,故產生了此次問題.

此次問題做一個小結:

產生原因:

查詢數據庫的時候會建立一個到數據庫的連接, 熟稱數據庫session, 有事務的情況下, 這一次連接就處於一個事務中, rpc調用遠程服務,由於不是本地方法,故無法加入本地事務中,所以可以算作是另一個事務,那麽rpc所處的事務插入數據後事務就結束並提交了. 而getOne()方法所處的事務實際上並沒有完成,還受到事務配置的約束,又由於沒有配置事務的隔離屬性,故采用的mysql的隔離配置Repeatable Read, 而這個可重復讀的意思就是一個數據可以反復讀取, 並且讀取到的值不會發生變化, 這實際上就是說當建立此次事務的時候就不會再讀取到新的值了.那麽在事務中途rpc插入的數據也不在getOne()所處事務可以讀取的範圍內, 故讀取不到. 但是如果隔離的屬性是Read Committed的話,則可以讀取到已經提交的數據, rpc服務雖然是中途插入的數據,但是由於新插入的數據已經提交了事務,故依然可以被getOne()方法讀取到.

解決方法:

set global transaction isolation level read committed;

最簡單的方法是直接變更mysql的隔離配置為read committed,這樣就一切歸位了.如果無法變更數據庫隔離級別也依然有辦法,由於隔離配置的生效級別是首先按照程序中配置的級別,其次再看數據庫配置的,那麽在程序中變更隔離級別也可以.

class XXXService{
     @Transactional(isolation = Isolation.READ_COMMITTED)
    public void demo(){
          //一堆業務邏輯
           rpc.insertOne();   //dubbo調用遠程服務插入一條數據
           getOne();   //獲取剛才插入的數據
    
    }
  
}

問題到此就結束了,但是這裏有一個問題比較好奇,Reaptable Read是如何實現的呢,感覺像是開啟事務後就對數據庫進行了一個快照一樣,但是想想又不可能真這樣做,然後百度到了一個介紹mysql mvcc機制的文章.

簡單來說就是實際上在每行數據最後有2列隱藏列,一列代表數據的創建版本,一列代表數據的刪除版本,列中的值存放的是版本號,而這個版本號就是遞增的且和某個事務唯一對應,這樣只要查詢創建版本小於當前版本,刪除版本大於當前版本就可以了,換成白話就是查詢在當前版本之前創建,當前版本之後刪除的數據,這樣就可以保證對於當前版本能查詢到的數據的穩定不可變,而對於修改數據的操作,則是拆分為標記原來的數據為刪除,並插入一條修改後的數據,這樣就完美巧妙的保證修改數據讀取的穩定性.

事務隔離級別引發的"血案"