拋磚引玉:多個(gè)查詢需要在同一時(shí)刻進(jìn)行數(shù)據(jù)的修改,就會(huì)產(chǎn)生并發(fā)控制的問題。我們需要如何避免寫個(gè)問題從而保證我們的數(shù)據(jù)庫數(shù)據(jù)不會(huì)被破壞。
鎖的概念
讀鎖是共享的互相不阻塞的。多個(gè)事務(wù)在聽一時(shí)刻可以同時(shí)讀取同一資源,而相互不干擾。
寫鎖的排他的。一個(gè)寫鎖會(huì)阻塞其他寫鎖或讀鎖。出于安全考慮只有這樣才能保證在給定的時(shí)間里只有一個(gè)事務(wù)能夠執(zhí)行寫入,并防止其他事務(wù)讀取正寫入的同一資源。
鎖帶來的問題
通過鎖定機(jī)制可以實(shí)現(xiàn)事務(wù)的隔離性要求,使得事務(wù)可以并發(fā)的工作,同時(shí)也帶來了三個(gè)問題:臟讀,不可重復(fù)讀和丟失更新。
臟讀
臟數(shù)據(jù):未提交的數(shù)據(jù)
如果讀到了臟數(shù)據(jù)即一個(gè)事務(wù)可以讀取到另一個(gè)事務(wù)中未提交的數(shù)據(jù)那就違背了事務(wù)的隔離性。
所以臟讀是指在不同的事務(wù)下,當(dāng)前事務(wù)可以讀取到另外事務(wù)的未提交的數(shù)據(jù),簡(jiǎn)單來說就是可以讀取到臟數(shù)據(jù)。
演示:
初始狀態(tài):
將會(huì)話A,B設(shè)置隔離級(jí)別為RU
set session transaction isolation level READ UNCOMMITTED;
會(huì)話A插入一條數(shù)據(jù)
這時(shí)候事務(wù)B在此執(zhí)行查詢操作,會(huì)發(fā)現(xiàn)事務(wù)B讀取到了事務(wù)A新增的數(shù)據(jù)。注意:此時(shí)事務(wù)A沒有提交。
不可重復(fù)讀
在一個(gè)事務(wù)中兩次讀取到的數(shù)據(jù)是不一樣的,這中情況被稱為不可重復(fù)讀。
與臟讀的區(qū)別:臟讀是讀取到了未提交的數(shù)據(jù),而不可重復(fù)讀是讀取到的卻是已經(jīng)提交的數(shù)據(jù),但是違反了數(shù)據(jù)庫事務(wù)一致性的要求。
演示:
事務(wù)B插入一條數(shù)據(jù)并且提交
事務(wù)A在此執(zhí)行select語句,事務(wù)A讀取到了事務(wù)B提交的數(shù)據(jù)
一般來說不可重復(fù)讀問題是可以接受的,因?yàn)樽x取到的是已經(jīng)提交的數(shù)據(jù),本身不會(huì)帶來什么問題。例如Oracle 和 SQL Server的默認(rèn)的事務(wù)隔離級(jí)別就是RC。 MySQL默認(rèn)的事務(wù)隔離級(jí)別是RR。
在MySQL InnoDB中通過使用 Next-key lock 算法來避免不可重復(fù)讀問題,并且將不可重復(fù)讀問題定義為幻讀(Phantom problem)
丟失更新
一個(gè)數(shù)據(jù)的更新會(huì)被另一個(gè)事務(wù)的更新操作所覆蓋,從而導(dǎo)致數(shù)據(jù)的不一致性。
第一種丟失:
A事務(wù)撤銷時(shí),把已經(jīng)提交的B事務(wù)的更新數(shù)據(jù)覆蓋了。這種錯(cuò)誤可能造成很嚴(yán)重的問題,通過下面的賬戶取款轉(zhuǎn)賬就可以看出來:
時(shí)間 | 取款事務(wù)A | 轉(zhuǎn)賬事務(wù)B |
---|---|---|
T1 | 開始事務(wù) | |
T2 | 開始事務(wù) | |
T3 | 查詢賬戶余額為1000元 | |
T4 | 查詢賬戶余額為1000元 | |
T5 | 匯入100元把余額改為1100元 | |
T6 | 提交事務(wù) | |
T7 | 取出100元把余額改為900元 | |
T8 | 撤銷事務(wù) | |
T9 | 余額恢復(fù)為1000 元(丟失更新) |
第二類丟失更新
A事務(wù)覆蓋B事務(wù)已經(jīng)提交的數(shù)據(jù),造成B事務(wù)所做操作丟失:
時(shí)間 | 轉(zhuǎn)賬事務(wù)A | 取款事務(wù)B |
---|---|---|
T1 | 開始事務(wù) | |
T2 | 開始事務(wù) | |
T3 | 查詢賬戶余額為1000元 | |
T4 | 查詢賬戶余額為1000元 | |
T5 | 取出100元把余額改為900元 | |
T6 | 提交事務(wù) | |
T7 | 匯入100元 | |
T8 | 提交事務(wù) | |
T9 | 把余額改為1100 元(丟失更新) |
要避免丟失更新的發(fā)生,需要讓事務(wù)在這種情況的操作變成串行化,而不是并行操作。即上面的select操作中加上排他鎖。
MySQL鎖的分類:
按照鎖的粒度來說
MySQL主要包含三種類型(級(jí)別)的鎖定機(jī)制:
全局鎖:鎖的是整個(gè)database。
表級(jí)鎖:鎖的是某個(gè)table。 (表排他鎖,表共享鎖,元數(shù)據(jù)鎖,自增鎖)
行級(jí)鎖:鎖的是某行數(shù)據(jù),也可能鎖定行之間的間隙。由某些存儲(chǔ)引擎實(shí)現(xiàn),比如InnoDB。
InnoDB的行級(jí)鎖,按照鎖定范圍來說
分為四種:
1.記錄鎖(Record Locks):鎖定索引中一條記錄。
2.間隙鎖(Gap Locks):要么鎖住索引記錄中間的值,要么鎖住第一個(gè)索引記錄前面的值或者最后一個(gè)索
引記錄后面的值。
3.臨鍵鎖(Next-Key Locks):是索引記錄上的記錄鎖和在索引記錄之前的間隙鎖的組合(間隙鎖+記錄
鎖)。
4.插入意向鎖(Insert Intention Locks):做insert操作時(shí)添加的對(duì)記錄id的鎖。
InnoDB的行級(jí)鎖,按照功能來說
分為兩種:
1.共享鎖(S):允許一個(gè)事務(wù)去讀一行,阻止其他事務(wù)獲得相同數(shù)據(jù)集的排他鎖。
2.排他鎖(X):允許獲得排他鎖的事務(wù)更新數(shù)據(jù),阻止其他事務(wù)取得相同數(shù)據(jù)集的共享讀鎖和排他寫鎖。
InnoDB行鎖是通過給索引上的索引項(xiàng)加鎖來實(shí)現(xiàn)的,因此InnoDB這種行鎖實(shí)現(xiàn)特點(diǎn)意味著:只有通過
索引條件檢索的數(shù)據(jù),InnoDB才使用行級(jí)鎖,否則,InnoDB將使用表鎖!
注意:插入意向鎖
(1)插入意向鎖是一種Gap鎖,不是意向鎖,在insert操作時(shí)產(chǎn)生。
(2)在多事務(wù)同時(shí)寫入不同數(shù)據(jù)至同一索引間隙的時(shí)候,并不需要等待其他事務(wù)完成,不會(huì)發(fā)生鎖等 待。
(3)假設(shè)有一個(gè)記錄索引包含鍵值4和7,不同的事務(wù)分別插入5和6,每個(gè)事務(wù)都會(huì)產(chǎn)生一個(gè)加在4-7之 間的插入意向鎖,獲取在插入行上的排它鎖,但是不會(huì)被互相鎖住,因?yàn)閿?shù)據(jù)行并不沖突。文章來源:http://www.zghlxwxcb.cn/news/detail-645987.html
(4)插入意向鎖不會(huì)阻止任何鎖,對(duì)于插入的記錄會(huì)持有一個(gè)記錄鎖。文章來源地址http://www.zghlxwxcb.cn/news/detail-645987.html
到了這里,關(guān)于MySQL中的鎖機(jī)制的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!