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