前言
上週五,一同事在開發時遇到了一個問題,叫我幫忙看下.在描述這個同事遇到問題之前,我先簡單做一些知識的鋪墊,否則不好描述.這裡面涉及到的知識點有Spring的事務傳播機制
、資料庫的隔離級別
等.
本篇重點是解決同事遇到的問題,因為這兩個知識點都先簡單談談,只為引出主題.後面這兩個會專門用一篇來講
Spring的事務傳播機制
Sring的事務傳播機制有七種,本文涉及到的有兩種
REQUIRED
: 用得最多(估計高達90%),也是預設的模式.若當前沒有事務,則新建事務,若當前已存在一個事務, 則加入到該事務中
REQUIRES_NEW
: 新建一個事務
Spring的事務傳播機制.後面我會用一篇文章專門來講,給大家列各種典型的題型,按照高中的題型訓練模式,徹底弄懂Spring的傳播機制,即使在各種複雜巢狀
+ try
場景下也能雲淡風輕地確定回滾情況.包括解析之前非常經典的一道面試題
- 做51次操作,前面50次成功,第51次失敗,全部回滾
- 做51次操作,前面50次成功,第51次失敗,只回滾第51次,全面50次照常提交
事務的隔離級別
考慮到部分同學英文問題,我特意標記了中文
1.Read Uncommitted
: 讀未提交
這個基本不可能用,從字面意思你就知道了,讀到別人還未提交的資料,別人都沒提交,你怎麼知道別人接下來是要提交還是回滾?既然不知道你就讀,自然會有問題.這個問題就是我們說的髒讀
.
2.Serializable
: 序列化
其實就是同步化,效能太差,基本不可能用這個.但是這個是唯一能解決幻讀
問題的.
畫圖分析
剩下兩種隔離級別
-
Read Committed
: 讀已提交 -
Read Repeatable
: 可重複讀
就比較重要了,我們畫圖來分析
比如我問,當t4
時,查詢出來的資料,name是大肥朝
還是toby
.其實你內心就不是很確定了,但是我如果把提交事務
這幾個字加粗標紅,然後引誘一下,絕大多數同學**根本把持不住!!!
**
既然都提交事務
了,那讀出來的自然是toby
啦.
其實t4
讀出來是toby
還是大肥朝
這個取決與你用的是讀已提交
、可重複讀
.其實你從這個中文名稱都可以猜到了,如果是讀已提交
,那麼查出來的就是toby
.如果是可重複讀
.讀出來的自然就是大肥朝
.
MySQL
預設用的是可重複讀
.Oracle
預設用的是讀已提交
.
溫馨提示: 這個後面也可以考慮再寫一篇詳細講一下這個隔離級別,因為一般問到可重複讀
,有深度的面試官會繼續追問,可重複讀是如何實現的?具體怎麼實現的我們可以先關注肥朝公眾號,後面再具體說資料庫的MVCC
機制
問題描述
由於業務具有一定的複雜度,不利於大家觀看,因此我這裡特意抽象簡化了模型.
首先,我先把資料庫的隔離級別改成讀已提交
.截圖為證:
那麼問題來了,請問步驟3查詢出來的資料是什麼呢.我們一起來見證
##見證答案
1.查詢完資料,準備更新資料
2.更新完成提交了事務,我們看得出此時資料庫已經改成了toby
3.震驚!查出來的資料竟然是大肥朝
這個時候似乎就不厚道了,肥朝你前面怎麼說的,你前面說讀已提交
讀出來的是別人已經提交的,那麼應該是toby
才對啊,怎麼還是大肥朝
?.
開始排查
我們把配置檔案設定成`debug級別一切就豁然開朗了
logging.level.com.toby.demo.dao=debug
logging.level.org.mybatis=debug
複製程式碼
這裡我特意用不同顏色給大家標記清楚了,其實看到只輸出兩次SQL
日誌大家就知道了.第三次查詢沒有輸出SQL,這很明顯是用到了Mybatis
的快取了.再根據我標記的SqlSession
資訊來看,這裡就是用到了Mybatis
的一級快取
解決辦法
解決辦法有很多,我們知道Mybatis一級快取的作用域是SqlSession
,那麼只要兩次查詢是不同的SqlSession
那自然這個一級快取就失效了.一級快取失效了.就會查詢兩次,輸出兩次sql.這個時候是toby
還是大肥朝
就真的取決於我前面說的隔離級別
了.比如一個簡單的改法是
改法有很多,具體根據那麼的業務來就可以了,當然也可以採用上面那種
寫在最後
肥朝 是一個專注於 原理、原始碼、開發技巧的技術公眾號,號內原創專題式原始碼解析、真實場景原始碼原理實戰(重點)。掃描下面二維碼關注肥朝,讓本該造火箭的你,不再擰螺絲!