事務(wù)丟失更新問題及樂觀鎖、悲觀鎖機(jī)制

學(xué)習(xí)計(jì)劃的第四天,仍然是對(duì)數(shù)據(jù)庫(kù)事務(wù)方面進(jìn)行學(xué)習(xí)。畢竟數(shù)據(jù)庫(kù)操作在后端開發(fā)中有著舉足輕重的作用。
那么,今天的學(xué)習(xí)內(nèi)容是:事務(wù)丟失更新問題及樂觀鎖、悲觀鎖機(jī)制。
話不多說(shuō),進(jìn)入正題。
什么是事務(wù)的丟失更新問題?
兩個(gè)或多個(gè)事務(wù)更新同一行,但這些事務(wù)彼此之間都不知道其它事務(wù)進(jìn)行的修改,因此第二個(gè)更改覆蓋了第一個(gè)修改 。
這樣說(shuō)太抽象,舉個(gè)例子:在數(shù)據(jù)庫(kù)表中存在一條數(shù)據(jù)

id:100
name:張散
age:20

此時(shí),兩個(gè)管理員同時(shí)查詢到了這條數(shù)據(jù),此時(shí),A管理員發(fā)現(xiàn)該數(shù)據(jù)的名字出錯(cuò)了,于是更新數(shù)據(jù),將該數(shù)據(jù)改為

id:100
name:張三
age:20

然后A管理員提交了更新操作。這個(gè)時(shí)候,B管理員也發(fā)現(xiàn)該數(shù)據(jù)的年齡出錯(cuò)了,于是準(zhǔn)備進(jìn)行更改,但是B管理員根本就不知道A管理員進(jìn)行了姓名的更改,于是B管理員進(jìn)行了如下修改

id:100
name:張散
age:21

然后B管理員提交了更新操作。操作完成后,數(shù)據(jù)庫(kù)表的數(shù)據(jù)變?yōu)榱?/p>

id:100
name:張散
age:21

這時(shí)候問題就出現(xiàn)了,A管理員發(fā)現(xiàn)姓名出錯(cuò)進(jìn)行了修改,而B管理員卻把正確的名字給改了回去,B管理員的修改就覆蓋了A管理員的修改,這種現(xiàn)象就是丟失更新。
貼張圖方便大家理解。


在這里插入圖片描述

那么該如何解決丟失更新問題呢?(丟失更新問題的解決)

  • 悲觀鎖(Pessimistic Locking)

  • 樂觀鎖(Optimistic Locking)

現(xiàn)在來(lái)依次解讀兩個(gè)方法解決丟失更新問題。
悲觀鎖原理:使用數(shù)據(jù)庫(kù)內(nèi)部鎖機(jī)制,進(jìn)行數(shù)據(jù)庫(kù)表的鎖定。就是在A管理員修改數(shù)據(jù)時(shí),A管理員就將數(shù)據(jù)鎖定,此時(shí)B管理員無(wú)法進(jìn)行修改、查詢。避免兩個(gè)事務(wù)同時(shí)修改,也就解決了丟失更新問題。
很多人覺得這個(gè)名字很奇怪,為什么要叫悲觀鎖,可以順帶解釋一下,因?yàn)槲覀兗僭O(shè)丟失更新會(huì)發(fā)生,是一個(gè)悲觀的態(tài)度。

還是舉個(gè)例子實(shí)現(xiàn)一下:
在MySQL中,默認(rèn)情況下,當(dāng)你修改數(shù)據(jù),會(huì)自動(dòng)地為數(shù)據(jù)加鎖,以防止兩個(gè)事務(wù)同時(shí)修改數(shù)據(jù),但是有個(gè)前提,就是必須在事務(wù)中才會(huì)自動(dòng)加鎖,事務(wù)和鎖是不可分開的,鎖一定是在事務(wù)中才能使用,當(dāng)事務(wù)關(guān)閉鎖自動(dòng)釋放。
開啟兩個(gè)MySQL窗口,并查詢表數(shù)據(jù)


在這里插入圖片描述

然后在兩個(gè)窗口均開啟一個(gè)事務(wù),接下來(lái)在左邊窗口輸入

update account set money = money - 100 where name = 'ccc';

但是不要提交,然后兩邊分別查詢數(shù)據(jù)


在這里插入圖片描述

這個(gè)情況肯定是可以理解的,因?yàn)樽筮叴翱谖刺峤?,所以右邊窗口?shù)據(jù)未改變。此時(shí),在B窗口輸入

update account set money = money - 100 where name = 'ccc';

會(huì)發(fā)現(xiàn),當(dāng)你執(zhí)行該條語(yǔ)句后,數(shù)據(jù)庫(kù)被阻塞了,因?yàn)閿?shù)據(jù)被加上了鎖,不允許兩個(gè)事務(wù)同時(shí)修改。但是在左邊窗口是可以進(jìn)行數(shù)據(jù)修改的,右邊窗口才會(huì)進(jìn)入阻塞狀態(tài)。
需要注意的是,雖然右邊窗口無(wú)法進(jìn)行修改,但是是可以進(jìn)行查詢的,所以這把鎖是讀鎖。
由此,我們來(lái)了解一下MySQL的鎖類型。
在MySQL內(nèi)部有兩種常用的鎖,一種叫讀鎖,又叫共享鎖;另一種叫寫鎖,也叫排它鎖。一張表可以添加多個(gè)讀鎖,如果某張表添加了讀鎖,但又不是當(dāng)前事務(wù)添加的,該表就不允許修改。添加讀鎖的sql語(yǔ)句為

select * from table lock in share mode;

演示一下。
在左邊窗口開啟一個(gè)事務(wù),然后輸入

select * from account lock in share mode;

此時(shí)該表就被加上了讀鎖,只允許加鎖的事務(wù)修改。
現(xiàn)在,在兩個(gè)窗口分別重新開啟一個(gè)事務(wù),然后分別輸入

 select * from account lock in share mode;

此時(shí),該表就被加上了兩把讀鎖,這時(shí)候兩個(gè)窗口都將不能修改表數(shù)據(jù)。如果在兩個(gè)窗口分別執(zhí)行更新語(yǔ)句,將會(huì)因?yàn)閮蓚€(gè)窗口都在互相等待對(duì)方釋放鎖從而發(fā)生死鎖問題。
強(qiáng)調(diào)一下,讀鎖是非常容易發(fā)生死鎖問題的。
接下來(lái)要分析的便是寫鎖了。

要注意,一張表只能加一個(gè)排它鎖,排它鎖和其它的共享鎖都具有互斥效果。通俗一點(diǎn)說(shuō)就是,一張表如果想加排它鎖,在它之前,就不能加別的共享鎖和排它鎖。添加寫鎖的sql語(yǔ)句為

select * from table for update;

當(dāng)一張表在一個(gè)事務(wù)中加上了寫鎖后,別的事務(wù)將不能夠修改該表數(shù)據(jù),因?yàn)樾薷臄?shù)據(jù)會(huì)自動(dòng)加上讀鎖,進(jìn)而產(chǎn)生互斥。
那既然有兩種方法可以解決丟失更新問題,那到底選擇哪種方法是關(guān)鍵。
其實(shí)解決丟失更新問題是不能用讀鎖去解決的,而應(yīng)該使用寫鎖解決,原因前面也說(shuō)了,讀鎖的弊端是顯而易見的。

我們可以使用寫鎖實(shí)現(xiàn)悲觀鎖來(lái)解決丟失更新問題。在原來(lái)的案例上加上寫鎖,B管理員在查詢時(shí)會(huì)因?yàn)锳管理員在修改數(shù)據(jù)而等待,直到A管理員提交了事務(wù)之后,鎖被釋放,此時(shí)B管理員允許查詢,查詢到的是A管理員修改后的數(shù)據(jù),從而避免了丟失更新問題。

樂觀鎖原理:使用的不是數(shù)據(jù)庫(kù)的鎖機(jī)制,而是一個(gè)特殊標(biāo)記字段,通過(guò)控制字段狀態(tài)和內(nèi)容得知數(shù)據(jù)是否發(fā)生了并發(fā)訪問,正如其名,我們假設(shè)丟失更新不會(huì)發(fā)生,是一個(gè)樂觀的態(tài)度,所以名為樂觀鎖。那特殊標(biāo)記字段是什么呢?在實(shí)際開發(fā)中,一般用到該字段timestamp,(時(shí)間戳字段)。
演示一下。
創(chuàng)建一張表,并初始化數(shù)據(jù)

create table blog(
    id int primary key,
    title varchar(40),
    updatetime timestamp
);

insert into blog values(1,'java學(xué)習(xí)',null);

創(chuàng)建成功后,查詢表數(shù)據(jù)


‘’

會(huì)發(fā)現(xiàn),timestamp字段在插入時(shí)自動(dòng)生成當(dāng)前時(shí)間。
當(dāng)你修改數(shù)據(jù)后再次查詢,會(huì)發(fā)現(xiàn)timestamp字段會(huì)自動(dòng)更新為當(dāng)前時(shí)間。
根據(jù)該原理,上面的案例中,可以在數(shù)據(jù)表中新增一個(gè)timestamp類型的updatetime字段,然后A、B管理員分別查詢數(shù)據(jù),然后編寫程序,比較用戶提交的updatetime與數(shù)據(jù)表中的updatetime是否一致,如果一致,證明在你修改數(shù)據(jù)的過(guò)程中其他人沒有修改過(guò)。而當(dāng)A管理員提交修改后,updatetime字段會(huì)自動(dòng)更新為當(dāng)前時(shí)間,再當(dāng)B管理員修改數(shù)據(jù)并提交時(shí),程序比較updatetime字段,發(fā)現(xiàn)兩者并不一樣,證明數(shù)據(jù)已經(jīng)被修改了,此時(shí)應(yīng)該通知B管理員重新修改。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容