mysql隔离级别与悲观锁、乐观锁
2019獨角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
1、什么是悲觀鎖,樂觀鎖
悲觀鎖大多數(shù)情況下依靠數(shù)據(jù)庫的鎖機(jī)制實現(xiàn),以保證操作最大程度的獨占性。但隨之而來的就是數(shù)據(jù)庫性能的大量開銷,特別是對長事務(wù)而言,這樣的開銷往往無法承受。而樂觀鎖機(jī)制在一定程度上解決了這個問題。樂觀鎖,大多是基于數(shù)據(jù)版本( Version )記錄機(jī)制實現(xiàn)。何謂數(shù)據(jù)版本?即為數(shù)據(jù)增加一個版本標(biāo)識,在基于數(shù)據(jù)庫表的版本解決方案中,一般是通過為數(shù)據(jù)庫表增加一個 “version” 字段來實現(xiàn)。讀取出數(shù)據(jù)時,將此版本號一同讀出,之后更新時,對此版本號加一。此時,將提交數(shù)據(jù)的版本數(shù)據(jù)與數(shù)據(jù)庫表對應(yīng)記錄的當(dāng)前版本信息進(jìn)行比對,如果提交的數(shù)據(jù)版本號大于數(shù)據(jù)庫表當(dāng)前版本號,則予以更新,否則認(rèn)為是過期數(shù)據(jù)。
?
悲觀鎖:假定會發(fā)生并發(fā)沖突,屏蔽一切可能違反數(shù)據(jù)完整性的操作。[1]
樂觀鎖:假設(shè)不會發(fā)生并發(fā)沖突,只在提交操作時檢查是否違反數(shù)據(jù)完整性。[1]樂觀鎖不能解決臟讀的問題。
樂觀鎖應(yīng)用
1.?????使用自增長的整數(shù)表示數(shù)據(jù)版本號。更新時檢查版本號是否一致,比如數(shù)據(jù)庫中數(shù)據(jù)版本為6,更新提交時version=6+1,使用該version值(=7)與數(shù)據(jù)庫version+1(=7)作比較,如果相等,則可以更新,如果不等則有可能其他程序已更新該記錄,所以返回錯誤。
2.?????使用時間戳來實現(xiàn).
注:對于以上兩種方式,Hibernate自帶實現(xiàn)方式:在使用樂觀鎖的字段前加annotation: @Version, Hibernate在更新時自動校驗該字段。
悲觀鎖應(yīng)用
需要使用數(shù)據(jù)庫的鎖機(jī)制,比如SQL SERVER 的TABLOCKX(排它表鎖)?此選項被選中時,SQL? Server? 將在整個表上置排它鎖直至該命令或事務(wù)結(jié)束。這將防止其他進(jìn)程讀取或修改表中的數(shù)據(jù)。
數(shù)據(jù)庫鎖機(jī)制:
?????? 共享鎖:由讀表操作加上的鎖,加鎖后其他用戶只能獲取該表或行的共享鎖,不能獲取排它鎖,也就是說只能讀不能寫。
?? 排它鎖:由寫表操作加上的鎖,加鎖后其他用戶不能獲取該表或行的任何鎖。
鎖的范圍:
?? 行鎖:?對某行記錄加上鎖
?? 表鎖:?對整個表加上鎖
這樣組合起來就有,行級共享鎖,表級共享鎖,行級排他鎖,表級排他鎖。
?
結(jié)論
在實際生產(chǎn)環(huán)境里邊,如果并發(fā)量不大且不允許臟讀,可以使用悲觀鎖解決并發(fā)問題;但如果系統(tǒng)的并發(fā)非常大的話,悲觀鎖定會帶來非常大的性能問題,所以我們就要選擇樂觀鎖定的方法.
?
2、事務(wù)并發(fā)會產(chǎn)生什么問題
1)第一類丟失更新:在沒有事務(wù)隔離的情況下,兩個事務(wù)都同時更新一行數(shù)據(jù),但是第二個事務(wù)卻中途失敗退出,?導(dǎo)致對數(shù)據(jù)的兩個修改都失效了。
例如:
張三的工資為5000,事務(wù)A中獲取工資為5000,事務(wù)B獲取工資為5000,匯入100,并提交數(shù)據(jù)庫,工資變?yōu)?100,
隨后事務(wù)A發(fā)生異常,回滾了,恢復(fù)張三的工資為5000,這樣就導(dǎo)致事務(wù)B的更新丟失了。
2)臟讀:臟讀就是指當(dāng)一個事務(wù)正在訪問數(shù)據(jù),并且對數(shù)據(jù)進(jìn)行了修改,而這種修改還沒有提交到數(shù)據(jù)庫中,這時,另外一個事務(wù)也訪問這個數(shù)據(jù),然后使用了這個數(shù)據(jù)。
例如:
張三的工資為5000,事務(wù)A中把他的工資改為8000,但事務(wù)A尚未提交。
與此同時,
事務(wù)B正在讀取張三的工資,讀取到張三的工資為8000。
隨后,
事務(wù)A發(fā)生異常,而回滾了事務(wù)。張三的工資又回滾為5000。
最后,
事務(wù)B讀取到的張三工資為8000的數(shù)據(jù)即為臟數(shù)據(jù),事務(wù)B做了一次臟讀。
3)不可重復(fù)讀:是指在一個事務(wù)內(nèi),多次讀同一數(shù)據(jù)。在這個事務(wù)還沒有結(jié)束時,另外一個事務(wù)也訪問該同一數(shù)據(jù)。那么,在第一個事務(wù)中的兩次讀數(shù)據(jù)之間,由于第二個事務(wù)的修改,那么第一個事務(wù)兩次讀到的的數(shù)據(jù)可能是不一樣的。這樣就發(fā)生了在一個事務(wù)內(nèi)兩次讀到的數(shù)據(jù)是不一樣的,因此稱為是不可重復(fù)讀。
例如:
在事務(wù)A中,讀取到張三的工資為5000,操作沒有完成,事務(wù)還沒提交。
與此同時,
事務(wù)B把張三的工資改為8000,并提交了事務(wù)。
隨后,
在事務(wù)A中,再次讀取張三的工資,此時工資變?yōu)?000。在一個事務(wù)中前后兩次讀取的結(jié)果并不致,導(dǎo)致了不可重復(fù)讀。
4)第二類丟失更新:不可重復(fù)讀的特例。有兩個并發(fā)事務(wù)同時讀取同一行數(shù)據(jù),然后其中一個對它進(jìn)行修改提交,而另一個也進(jìn)行了修改提交。這就會造成第一次寫操作失效。?
例如:
在事務(wù)A中,讀取到張三的存款為5000,操作沒有完成,事務(wù)還沒提交。
與此同時,
事務(wù)B,存儲1000,把張三的存款改為6000,并提交了事務(wù)。
隨后,
在事務(wù)A中,存儲500,把張三的存款改為5500,并提交了事務(wù),這樣事務(wù)A的更新覆蓋了事務(wù)B的更新。
5)幻讀:是指當(dāng)事務(wù)不是獨立執(zhí)行時發(fā)生的一種現(xiàn)象,例如第一個事務(wù)對一個表中的數(shù)據(jù)進(jìn)行了修改,這種修改涉及到表中的全部數(shù)據(jù)行。同時,第二個事務(wù)也修改這個表中的數(shù)據(jù),這種修改是向表中插入一行新數(shù)據(jù)。那么,以后就會發(fā)生操作第一個事務(wù)的用戶發(fā)現(xiàn)表中還有沒有修改的數(shù)據(jù)行,就好象發(fā)生了幻覺一樣。
例如:
目前工資為5000的員工有10人,事務(wù)A讀取所有工資為5000的人數(shù)為10人。
此時,
事務(wù)B插入一條工資也為5000的記錄。
這是,事務(wù)A再次讀取工資為5000的員工,記錄為11人。此時產(chǎn)生了幻讀。
提醒:
不可重復(fù)讀的重點是修改:
同樣的條件,你讀取過的數(shù)據(jù),再次讀取出來發(fā)現(xiàn)值不一樣了
幻讀的重點在于新增或者刪除:
同樣的條件,第 1 次和第 2 次讀出來的記錄數(shù)不一樣
3、事務(wù)隔離級別,解決什么并發(fā)問題
(1)READ_UNCOMMITTED
這是事務(wù)最低的隔離級別,它充許別外一個事務(wù)可以看到這個事務(wù)未提交的數(shù)據(jù)。
會出現(xiàn)臟讀、不可重復(fù)讀、幻讀 (隔離級別最低,并發(fā)性能高)。
(2)READ_COMMITTED
保證一個事務(wù)修改的數(shù)據(jù)提交后才能被另外一個事務(wù)讀取。另外一個事務(wù)不能讀取該事務(wù)未提交的數(shù)據(jù)。
可以避免臟讀,但會出現(xiàn)不可重復(fù)讀、幻讀問題(鎖定正在讀取的行)。
(3)REPEATABLE_READ
可以防止臟讀、不可重復(fù)讀,但會出幻讀(鎖定所讀取的所有行)。
(4)SERIALIZABLE
這是花費最高代價但是最可靠的事務(wù)隔離級別,事務(wù)被處理為順序執(zhí)行。
保證所有的情況不會發(fā)生(鎖表)。
詳情見下表:
提醒:
Mysql默認(rèn)的事務(wù)隔離級別為repeatable_read
4、悲觀鎖和樂觀鎖與數(shù)據(jù)庫隔離級別的關(guān)系
關(guān)系總結(jié),事務(wù)隔離級別是并發(fā)控制的整體解決方案,其實際上是綜合利用各種類型的鎖和行版本控制,來解決并發(fā)問題。鎖是數(shù)據(jù)庫并發(fā)控制的內(nèi)部機(jī)制,是基礎(chǔ)。對用戶來說,只有當(dāng)事務(wù)隔離級別無法解決一些并發(fā)問題和需求時,才有必要在語句中手動設(shè)置鎖。
5、悲觀鎖和樂觀鎖的使用
1)悲觀鎖:
要使用悲觀鎖,我們必須關(guān)閉mysql數(shù)據(jù)庫的自動提交屬性,因為MySQL默認(rèn)使用autocommit模式,也就是說,當(dāng)你執(zhí)行一個更新操作后,MySQL會立刻將結(jié)果進(jìn)行提交。
使用mybatis或者jdbc api實現(xiàn)時:
sql語句:select status from t_goods where id=1 for update;,update t_goods set status=2;,這時id為1的記錄會被鎖住
使用hibernate實現(xiàn)時,
Criteria.setLockMode?
Query.setLockMode?
Session.lock?
注意,只有在查詢開始之前(也就是Hiberate 生成SQL 之前)設(shè)定加鎖,才會 真正通過數(shù)據(jù)庫的鎖機(jī)制進(jìn)行加鎖處理,否則,數(shù)據(jù)已經(jīng)通過不包含for update 子句的Select SQL加載進(jìn)來,所謂數(shù)據(jù)庫加鎖也就無從談起。
2)樂觀鎖:
樂觀鎖是系統(tǒng)層面的實現(xiàn),推薦的實現(xiàn)方案是表中新增一個version字段,然后基于version字段的更新來判斷事務(wù)是否有效。
使用mybatis或者jdbc api實現(xiàn)時:
select (status,status,version) from t_goods where id=#{id}
update t_goods?
set status=2,version=version+1
where id=#{id} and version=#{version};
更新操作是基于version的,當(dāng)事務(wù)A更新了記錄之后,version字段會加1,事務(wù)B再根據(jù)之前的version去查找記錄的時候,數(shù)據(jù)庫沒有相應(yīng)的記錄就會出現(xiàn)異常,這時系統(tǒng)捕捉異常進(jìn)行處理,即可避免并發(fā)的問題。
使用hibernate實現(xiàn)時:
Hibernate中必須指定optimistic-lock屬性對version描述符指定。
轉(zhuǎn)載于:https://my.oschina.net/u/2277088/blog/1630187
總結(jié)
以上是生活随笔為你收集整理的mysql隔离级别与悲观锁、乐观锁的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CSS三栏自适应布局,左中右,上中下
- 下一篇: mongo索引命令