mysql行级锁作用_Mysql事务及行级锁的理解
在最近的開發(fā)中,碰到一個需求簽到,每個用戶每天只能簽到一次,那么怎么去判斷某個用戶當(dāng)天是否簽到呢?因?yàn)楫?dāng)屬表設(shè)計(jì)的時候,每個用戶簽到一次,即向表中插入一條記錄,根據(jù)記錄的數(shù)量和時間來判斷用戶當(dāng)天是否簽到。
這樣的話就會有一個問題,如果是在網(wǎng)速過慢的情況下,用戶多次點(diǎn)擊簽到按鈕,那么變會發(fā)送多次請求,可能會導(dǎo)致一天多次簽到,重復(fù)提交的問題,那么很自然的想到用事務(wù)。這次用的是spring + mybtais的框架,一開始設(shè)計(jì)的代碼大致如下:
public booleansignIn(SignInHistory signInHistory) {//編程式開啟事務(wù)
TransactionTemplate template = newTransactionTemplate(transactionManager);boolean result = (boolean) template.execute(new TransactionCallback() {publicObject doInTransaction(TransactionStatus transactionStatus) {try{//獲取用戶所有簽到記錄
List SignInHistoryList =signInMapper.select(signInHistory);//如果當(dāng)前時間和List中某條簽到時間相同,則當(dāng)天已簽到,代碼略去//插入簽到歷史表
signInMapper.insert(signInHistory);
}catch(Exception e) {
transactionStatus.setRollbackOnly();
logger.error(e);return false;
}return true;
}
});
}
但是在測試中,發(fā)現(xiàn)還是會發(fā)生重復(fù)提交。
那么看mysql文檔
Consistent read is the default mode in whichInnoDBprocessesSELECTstatements inREAD COMMITTEDandREPEATABLE READisolation levels. A consistent read does not set any locks on the tables it accesses, and therefore other sessions are free to modify those tables at the same time a consistent read is being performed on the table.
Mysql文檔中也有相關(guān)說明:如果是在read committed和repeatab read下,普通的select語句并不會進(jìn)行鎖操作。其它session可以照常更新或插入操作。
所以在這里面就可以發(fā)現(xiàn),如果只是普通select,不管在不在事務(wù)中,mysql都不會將select加鎖,所以根本無法阻止其它事務(wù)插入記錄。
由此可以得出一個理解,
事務(wù)隔離級別
數(shù)據(jù)庫事務(wù)隔離級別,只是針對一個事務(wù)能不能讀取其它事務(wù)的中間結(jié)果。
Read Uncommitted(讀取未提交內(nèi)容)
在該隔離級別,所有事務(wù)都可以看到其他未提交事務(wù)的執(zhí)行結(jié)果。本隔離級別很少用于實(shí)際應(yīng)用,因?yàn)樗男阅芤膊槐绕渌墑e好多少。讀取未提交的數(shù)據(jù),也被稱之為臟讀(Dirty Read)。
Read Committed(讀取提交內(nèi)容)
這是大多數(shù)數(shù)據(jù)庫系統(tǒng)的默認(rèn)隔離級別(但不是MySQL默認(rèn)的)。它滿足了隔離的簡單定義:一個事務(wù)只能看見已經(jīng)提交事務(wù)所做的改變。這種隔離級別也支持所謂的不可重復(fù)讀(Nonrepeatable Read),因?yàn)橥皇聞?wù)的其他實(shí)例在該實(shí)例處理其間可能會有新的commit,所以同一select可能返回不同結(jié)果。
Repeatable Read(可重讀)
這是MySQL的默認(rèn)事務(wù)隔離級別,它確保同一事務(wù)的多個實(shí)例在并發(fā)讀取數(shù)據(jù)時,會看到同樣的數(shù)據(jù)行。不過理論上,這會導(dǎo)致另一個棘手的問題:幻讀(Phantom Read)。簡單的說,幻讀指當(dāng)用戶讀取某一范圍的數(shù)據(jù)行時,另一個事務(wù)又在該范圍內(nèi)插入了新行,當(dāng)用戶再讀取該范圍的數(shù)據(jù)行時,會發(fā)現(xiàn)有新的"幻影"行。InnoDB和Falcon存儲引擎通過多版本并發(fā)控制(MVCC,Multiversion Concurrency Control)機(jī)制解決了該問題。
Serializable(可串行化)
這是最高的隔離級別,它通過強(qiáng)制事務(wù)排序,使之不可能相互沖突,從而解決幻讀問題。簡言之,它是在每個讀的數(shù)據(jù)行上加上共享鎖。在這個級別,可能導(dǎo)致大量的超時現(xiàn)象和鎖競爭。
這四種隔離級別采取不同的鎖類型來實(shí)現(xiàn),若讀取的是同一個數(shù)據(jù)的話,就容易發(fā)生問題。例如:
臟讀(Drity Read):某個事務(wù)已更新一份數(shù)據(jù),另一個事務(wù)在此時讀取了同一份數(shù)據(jù),由于某些原因,前一個RollBack了操作,則后一個事務(wù)所讀取的數(shù)據(jù)就會是不正確的。
不可重復(fù)讀(Non-repeatable read):在一個事務(wù)的兩次查詢之中數(shù)據(jù)不一致,這可能是兩次查詢過程中間插入了一個事務(wù)更新的原有的數(shù)據(jù)。
幻讀(Phantom Read):在一個事務(wù)的兩次查詢中數(shù)據(jù)筆數(shù)不一致,例如有一個事務(wù)查詢了幾列(Row)數(shù)據(jù),而另一個事務(wù)卻在此時插入了新的幾列數(shù)據(jù),先前的事務(wù)在接下來的查詢中,就會發(fā)現(xiàn)有幾列數(shù)據(jù)是它先前所沒有的。
事務(wù)傳播級別
數(shù)據(jù)庫事務(wù)傳播級別,指的是事務(wù)嵌套時,應(yīng)該采用什么策略,即在一個事務(wù)中調(diào)用別的事務(wù),該怎么辦
假如有一下兩個事務(wù):
ServiceA?{
voidmethodA(){
ServiceB.methodB();
}
}ServiceB?{
voidmethodB(){
}
}
1:PROPAGATION_REQUIRED
加入當(dāng)前正要執(zhí)行的事務(wù)不在另外一個事務(wù)里,那么就起一個新的事務(wù)
比如說,ServiceB.methodB的事務(wù)級別定義為PROPAGATION_REQUIRED,那么由于執(zhí)行ServiceA.methodA的時候,
ServiceA.methodA已經(jīng)起了事務(wù),這時調(diào)用ServiceB.methodB,ServiceB.methodB看到自己已經(jīng)運(yùn)行在ServiceA.methodA
的事務(wù)內(nèi)部,就不再起新的事務(wù)。而假如ServiceA.methodA運(yùn)行的時候發(fā)現(xiàn)自己沒有在事務(wù)中,他就會為自己分配一個事務(wù)。
這樣,在ServiceA.methodA或者在ServiceB.methodB內(nèi)的任何地方出現(xiàn)異常,事務(wù)都會被回滾。即使ServiceB.methodB的事務(wù)已經(jīng)被
提交,但是ServiceA.methodA在接下來fail要回滾,ServiceB.methodB也要回滾
2:PROPAGATION_SUPPORTS
如果當(dāng)前在事務(wù)中,即以事務(wù)的形式運(yùn)行,如果當(dāng)前不再一個事務(wù)中,那么就以非事務(wù)的形式運(yùn)行
3:PROPAGATION_MANDATORY
必須在一個事務(wù)中運(yùn)行。也就是說,他只能被一個父事務(wù)調(diào)用。否則,他就要拋出異常
4:PROPAGATION_REQUIRES_NEW
這個就比較繞口了。比如我們設(shè)計(jì)ServiceA.methodA的事務(wù)級別為PROPAGATION_REQUIRED,ServiceB.methodB的事務(wù)級別為PROPAGATION_REQUIRES_NEW,
那么當(dāng)執(zhí)行到ServiceB.methodB的時候,ServiceA.methodA所在的事務(wù)就會掛起,ServiceB.methodB會起一個新的事務(wù),等待ServiceB.methodB的事務(wù)完成以后,
他才繼續(xù)執(zhí)行。他與PROPAGATION_REQUIRED的事務(wù)區(qū)別在于事務(wù)的回滾程度了。因?yàn)镾erviceB.methodB是新起一個事務(wù),那么就是存在
兩個不同的事務(wù)。如果ServiceB.methodB已經(jīng)提交,那么ServiceA.methodA失敗回滾,ServiceB.methodB是不會回滾的。如果ServiceB.methodB失敗回滾,
如果他拋出的異常被ServiceA.methodA捕獲,ServiceA.methodA事務(wù)仍然可能提交。
5:PROPAGATION_NOT_SUPPORTED
當(dāng)前不支持事務(wù)。比如ServiceA.methodA的事務(wù)級別是PROPAGATION_REQUIRED,而ServiceB.methodB的事務(wù)級別是PROPAGATION_NOT_SUPPORTED,
那么當(dāng)執(zhí)行到ServiceB.methodB時,ServiceA.methodA的事務(wù)掛起,而他以非事務(wù)的狀態(tài)運(yùn)行完,再繼續(xù)ServiceA.methodA的事務(wù)。
6:PROPAGATION_NEVER
不能在事務(wù)中運(yùn)行。假設(shè)ServiceA.methodA的事務(wù)級別是PROPAGATION_REQUIRED,而ServiceB.methodB的事務(wù)級別是PROPAGATION_NEVER,
那么ServiceB.methodB就要拋出異常了。
7:PROPAGATION_NESTED
理解Nested的關(guān)鍵是savepoint。他與PROPAGATION_REQUIRES_NEW的區(qū)別是,PROPAGATION_REQUIRES_NEW另起一個事務(wù),將會與他的父事務(wù)相互獨(dú)立,
而Nested的事務(wù)和他的父事務(wù)是相依的,他的提交是要等和他的父事務(wù)一塊提交的。也就是說,如果父事務(wù)最后回滾,他也要回滾的。
而Nested事務(wù)的好處是他有一個savepoint。
行級鎖
如果有兩個事務(wù)A,B都有read和write操作,如果邏輯是如果表中沒有記錄則插入,那么因?yàn)閞ead操作并沒有加鎖,A,B進(jìn)行read操作時,有可能表中都沒有記錄,那么事務(wù)A,B都會進(jìn)行插入操作,表中將會有兩條記錄。
如果要保證在事務(wù)并發(fā)時,每條事務(wù)讀取到的數(shù)據(jù)都是最新的,那么只能采用鎖。
在select語句后加上FOR UPDATE,再測試,重復(fù)提交的問題被解決了。
但是問題又來了,如果在select語句后加上LOCK IN SHARE MODE,那么會報(bào)死鎖的錯誤。
查看mysql文檔:
SELECT ... LOCK IN SHARE MODEsets a shared mode lock on any rows that are read. Other sessions can read the rows, but cannot modify them until your transaction commits. If any of these rows were changed by another transaction that has not yet committed, your query waits until that transaction ends and then uses the latest values.
SELECT ... FORUPDATEsets an exclusive lock on the rows read. An exclusive lock prevents other sessions from accessing the rows for reading or writing.
LOCK IN SHARE MODE會在讀取的行上加共享鎖,其他session只能讀不能修改或刪除,如果有其他事務(wù)修改了記錄,那么會等待事務(wù)提交后,再讀取。
FOR UPDATE在讀取行上設(shè)置一個排他鎖。阻止其他session讀取或者寫入行數(shù)據(jù)
這樣看起來似乎就能解釋為什么使用LOCK IN SHARE MODE會產(chǎn)生死鎖了,假如兩個事務(wù)A、B都讀取同一行記錄,那么在這一行就加上了共享鎖,但是A和B事務(wù)中都需要修改這一行,那么都要等待對方釋放共享鎖才能進(jìn)行,結(jié)果造成了死鎖。
只能使用for update來防止死鎖和重復(fù)插入。
這就是mysql的兩種行級鎖的區(qū)別。
總結(jié)
以上是生活随笔為你收集整理的mysql行级锁作用_Mysql事务及行级锁的理解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 急用钱定期理财怎么取?介绍2种提前支取方
- 下一篇: 理财赎回几天到账?理财赎回到账时间