我們在工作中有一項業(yè)務,如果A修改了,那么B就要進行更新,如果A有多個呢?B就會有很多更新語句,比如當前用戶money=10,有很多個請求都來更新用戶money +10,這樣怎么辦呢?
mysql的事務隔離等級
事務 | 事務說明 | 臟讀 | 不可重讀 | 幻讀 |
---|---|---|---|---|
READ_UNCOMMITTED | 讀取未提交內容 | √ | √ | √ |
READ_COMMITTED | 讀取提交內容 | × | √ | √ |
REPEATABLE_READ | 可重讀 | × | × | √ |
SERIALIZABLE | 可串行化 | × | × | × |
事務介紹
- 臟讀
臟讀又稱無效數(shù)據(jù)的讀出,是指在數(shù)據(jù)庫訪問中,事務T1將某一值修改,然后事務T2讀取該值,此后T1因為某種原因撤銷對該值的修改,這就導致了T2所讀取到的數(shù)據(jù)是無效的。
臟讀就是指當一個事務正在訪問數(shù)據(jù),并且對數(shù)據(jù)進行了修改,而這種修改還沒有提交到數(shù)據(jù)庫中,這時,另外一個事務也訪問這個數(shù)據(jù),然后使用了這個數(shù)據(jù)。因為這個數(shù)據(jù)是還沒有提交的數(shù)據(jù),那么另外一個事務讀到的這個數(shù)據(jù)是臟數(shù)據(jù),依據(jù)臟數(shù)據(jù)所做的操作可能是不正確的。 - 不可重復讀
不可重復讀,是指在數(shù)據(jù)庫訪問中,一個事務范圍內兩個相同的查詢卻返回了不同數(shù)據(jù)。
這是由于查詢時系統(tǒng)中其他事務修改的提交而引起的。比如事務T1讀取某一數(shù)據(jù),事務T2讀取并修改了該數(shù)據(jù),T1為了對讀取值進行檢驗而再次讀取該數(shù)據(jù),便得到了不同的結果。 - 幻讀
簡單的說,幻讀指當用戶讀取某一范圍的數(shù)據(jù)行時,另一個事務又在該范圍內插入了新行,當用戶再讀取該范圍的數(shù)據(jù)行時,會發(fā)現(xiàn)有新的“幻影” 行
選型與實驗.
我們的場景需要保障最終的數(shù)據(jù)的正確性, 兩個事務都開啟之后,必須保障修改數(shù)據(jù)之后提交能夠正確,這里我們選擇READ_COMMITTED允許不可重讀,或者REPEATABLE_READ 允許幻讀.
并發(fā)測試
我們使用java進行 一個用戶的查詢和更新操作,使用的是JPA,采用并發(fā)10,每次加10進行測試. 選取的級別是可重讀. 在并發(fā)狀態(tài)下 數(shù)據(jù)為40,很明顯無法保障數(shù)據(jù)的正確性.
并發(fā)會帶來意想不到的狀況,如何解決并發(fā)帶來的問題!!!!
我們的業(yè)務場景無法避免并發(fā)問題,mysql提供了鎖的機制,鑒于我們一般使用jpa,這里,我們通過JPA來檢驗下鎖的機制.
- 樂觀鎖
假設有個業(yè)務他們之間一般不會造成并發(fā),假如并發(fā)了就回滾. - 悲觀鎖
假設業(yè)務之間存在并發(fā)沖突.請求會鎖定,使用后會解鎖. - 鎖類型(LockMode),由于我們使用的是mysql,一般我們使用悲觀排它鎖解決并發(fā)問題. PESSIMISTIC_WRITE
tips: 我們在使用中要
- 關閉mysql自動提交功能
- innodb_lock_wait_timeout 超時時間設置為300'\
- show status like ‘table%’; 查看 Table_locks_waited
- show status like 'innodb_row_lock%';
- 解鎖: show processlist; 找到進程 kill id; (或者UNLOCK tables)
- LOCK TABLES tbl_name READ; 鎖表操作.
該業(yè)務場景我們要讀取B用戶,然后修改B用戶,我們在讀取用戶B時候加上鎖,這個時候所有外部的訪問將要等待處理完成之后才能夠獲取B用戶信息.
public interface UserRepository extends CrudRepository<User, Long> {
@Lock(LockModeType.PESSIMISTIC_WRITE)
User findOne(Long id);
}
w