mysql隔离级别底层实现_1、深入理解mysql四种隔离级别及底层实现原理(MVCC和锁)...
一、ACID特性
持久性,我們就不講了,易懂。
1、原子性
在同一個(gè)事務(wù)內(nèi)部的一組操作必須全部執(zhí)行成功(或者全部失敗)。
為了保證事務(wù)操作的原子性,必須實(shí)現(xiàn)基于日志的REDO/UNDO機(jī)制:將所有對數(shù)據(jù)的更新操作都寫入日志,如果一個(gè)事務(wù)中的一部分操作已經(jīng)成功,但以后的操作,由于斷電/系統(tǒng)崩潰/其它的軟硬件錯(cuò)誤而無法繼續(xù),則通過回溯日志,將已經(jīng)執(zhí)行成功的操作撤銷,從而達(dá)到“全部操作失敗”的目的。 最常見的場景是,數(shù)據(jù)庫系統(tǒng)崩潰后重啟,此時(shí)數(shù)據(jù)庫處于不一致的狀態(tài),必須先執(zhí)行一個(gè)crash recovery的過程:讀取日志進(jìn)行REDO(重演將所有已經(jīng)執(zhí)行成功但尚未寫入到磁盤的操作,保證持久性),再對所有到崩潰時(shí)尚未成功提交的事務(wù)進(jìn)行UNDO(撤銷所有執(zhí)行了一部分但尚未提交的操作,保證原子性)。crash recovery結(jié)束后,數(shù)據(jù)庫恢復(fù)到一致性狀態(tài),可以繼續(xù)被使用。
某個(gè)應(yīng)用在執(zhí)行轉(zhuǎn)帳的數(shù)據(jù)庫操作時(shí),必須在同一個(gè)事務(wù)內(nèi)部調(diào)用對帳戶A和帳戶B的操作,才能保證數(shù)據(jù)的一致性。
但是,原子性并不能完全保證一致性。在多個(gè)事務(wù)并行進(jìn)行的情況下,即使保證了每一個(gè)事務(wù)的原子性,仍然可能導(dǎo)致數(shù)據(jù)不一致的結(jié)果。 例如,事務(wù)1需要將100元轉(zhuǎn)入帳號A:先讀取帳號A的值,然后在這個(gè)值上加上100。但是,在這兩個(gè)操作之間,另一個(gè)事務(wù)2修改了帳號A的值,為它增加了100元。那么最后的結(jié)果應(yīng)該是A增加了200元。但事實(shí)上,事務(wù)1最終完成后,帳號A只增加了100元,因?yàn)槭聞?wù)2的修改結(jié)果被事務(wù)1覆蓋掉了。
簡而言之,就是:原子性僅能夠保證單個(gè)事務(wù)的一致性。就像redis一樣,也只能保證單操作的線程安全,并不能保證多操作下的線程安全。
2、一致性
按照我個(gè)人的理解,在事務(wù)處理的ACID屬性中,一致性是最基本的屬性,其它的三個(gè)屬性都為了保證一致性而存在的。
我們舉個(gè)反例來理解下一致性概念。例如:從帳戶A轉(zhuǎn)一筆錢到帳戶B上,如果帳戶A上的錢減少了,而帳戶B上的錢卻沒有增加,那么我們認(rèn)為此時(shí)數(shù)據(jù)處于不一致的狀態(tài)。
為了保證并發(fā)情況下的一致性,引入了隔離性,即保證每一個(gè)事務(wù)能夠看到的數(shù)據(jù)總是一致的,就好象其它并發(fā)事務(wù)并不存在一樣。
3、隔離性
數(shù)據(jù)庫四種隔離級別,以及常見的幾種讀異常,大家應(yīng)該都是耳熟能詳?shù)?#xff0c;但數(shù)據(jù)庫底層是怎么實(shí)現(xiàn)隔離性的呢?都采用了哪些技術(shù)呢? 主要有兩個(gè)技術(shù):MVCC(多版本并發(fā)控制)和鎖。
(1)MVCC(多版本并發(fā)控制)
多版本并發(fā)控制,顧名思義,在并發(fā)訪問的時(shí)候,數(shù)據(jù)存在版本的概念,可以有效地提升數(shù)據(jù)庫并發(fā)能力,常見的數(shù)據(jù)庫如MySQL、MS SQL Server、IBM DB2、Hbase、MongoDB等等都在使用。
簡單講,如果沒有MVCC,當(dāng)想要讀取的數(shù)據(jù)被其他事務(wù)用排它鎖鎖住時(shí),只能互斥等待;而這時(shí)MVCC可以通過提供歷史版本從而實(shí)現(xiàn)讀取被鎖的數(shù)據(jù)的歷史版本,從而避免了互斥等待。
InnoDB采用的MVCC實(shí)現(xiàn)方式是:在需要時(shí),通過undo日志構(gòu)造出歷史版本。
(2)鎖
1) 鎖的分類
Shared Locks(共享鎖/S鎖)
若事務(wù)T對數(shù)據(jù)對象A加上S鎖,則事務(wù)T只能讀A;其他事務(wù)只能再對A加S鎖,而不能加X鎖,直到T釋放A上的S鎖。這就保證了其他事務(wù)可以讀A,但在T釋放A上的S鎖之前不能對A做任何修改。
Exclusive Locks(排它鎖/X鎖)
若事務(wù)T對數(shù)據(jù)對象A加上X鎖,則只允許T讀取和修改A,其它任何事務(wù)都不能再對A加任何類型的鎖,直到T釋放A上的鎖。它防止任何其它事務(wù)獲取資源上的鎖,直到在事務(wù)的末尾將資源上的原始鎖釋放為止。在更新操作(INSERT、UPDATE 或 DELETE)過程中始終應(yīng)用排它鎖。
注意:排他鎖會阻止其它事務(wù)再對其鎖定的數(shù)據(jù)加讀或?qū)懙逆i,但是不加鎖的就沒辦法控制了。
Record Locks(行鎖)
行鎖,顧名思義,是加在索引行(對!是索引行!不是數(shù)據(jù)行!)上的鎖。比如select * from user where id=1 and id=10 for update,就會在id=1和id=10的索引行上加Record Lock。
Gap Locks(間隙鎖)
間隙鎖,它會鎖住兩個(gè)索引之間的區(qū)域。比如select * from user where id>1 and id<10 for update,就會在id為(1,10)的索引區(qū)間上加Gap Lock。
Next-Key Locks(間隙鎖)
也叫間隙鎖,它是Record Lock + Gap Lock形成的一個(gè)閉區(qū)間鎖。比如select * from user where id>=1 and id<=10 for update,就會在id為[1,10]的索引閉區(qū)間上加Next-Key Lock。
這樣組合起來就有,行級共享鎖,表級共享鎖,行級排它鎖,表級排它鎖。
2) 什么時(shí)候會加鎖?
在數(shù)據(jù)庫增刪改查四種操作中,insert、delete和update都是會加排它鎖(Exclusive Locks)的,而select只有顯式聲明才會加鎖:
select: 即最常用的查詢,是不加任何鎖的
select ... lock in share mode: 會加共享鎖(Shared Locks)
select ... for update: 會加排它鎖
3) 四種隔離級別
不同的隔離級別是在數(shù)據(jù)可靠性和并發(fā)性之間的均衡取舍,隔離級別越高,對應(yīng)的并發(fā)性能越差,數(shù)據(jù)越安全可靠。
READ UNCOMMITTED
顧名思義,事務(wù)之間可以讀取彼此未提交的數(shù)據(jù)。機(jī)智如你會記得,在前文有說到所有寫操作都會加排它鎖,那還怎么讀未提交呢?
機(jī)智如你,前面我們介紹排它鎖的時(shí)候,有這種說明: 排他鎖會阻止其它事務(wù)再對其鎖定的數(shù)據(jù)加讀或?qū)懙逆i,但是對不加鎖的讀就不起作用了。
READ UNCOMMITTED隔離級別下, 讀不會加任何鎖。而寫會加排他鎖,并到事務(wù)結(jié)束之后釋放。
實(shí)例1:
查-寫:查并沒有阻止寫,表明查肯定并沒有加鎖,要不寫肯定就阻塞了。寫很明顯,會加排它鎖的。
實(shí)例2: 寫-寫:阻塞,表明,寫會加排它鎖。
READ COMMITTED
顧名思義,事務(wù)之間可以讀取彼此已提交的數(shù)據(jù)。
InnoDB在該隔離級別(READ COMMITTED)寫數(shù)據(jù)時(shí),使用排它鎖, 讀取數(shù)據(jù)不加鎖而是使用了MVCC機(jī)制。
因此,在讀已提交的級別下,都會通過MVCC獲取當(dāng)前數(shù)據(jù)的最新快照,不加任何鎖,也無視任何鎖(因?yàn)闅v史數(shù)據(jù)是構(gòu)造出來的,身上不可能有鎖)。
但是,該級別下還是遺留了不可重復(fù)讀和幻讀問題: MVCC版本的生成時(shí)機(jī): 是每次select時(shí)。這就意味著,如果我們在事務(wù)A中執(zhí)行多次的select,在每次select之間有其他事務(wù)更新了我們讀取的數(shù)據(jù)并提交了,那就出現(xiàn)了不可重復(fù)讀,即:重復(fù)讀時(shí),會出現(xiàn)數(shù)據(jù)不一致問題,后面我們會講解超支現(xiàn)象,就是這種引起的。
REPEATABLE READ
READ COMMITTED級別不同的是MVCC版本的生成時(shí)機(jī),即:一次事務(wù)中只在第一次select時(shí)生成版本,后續(xù)的查詢都是在這個(gè)版本上進(jìn)行,從而實(shí)現(xiàn)了可重復(fù)讀。
但是因?yàn)镸VCC的快照只對讀操作有效,對寫操作無效,舉例說明會更清晰一點(diǎn): 事務(wù)A依次執(zhí)行如下3條sql,事務(wù)B在語句1和2之間,插入10條age=20的記錄,事務(wù)A就幻讀了。
1. select count(1) from user where age=20;
-- return 0: 當(dāng)前沒有age=20的
2. update user set name=test where age=20;
-- Affects 10 rows: 因?yàn)槭聞?wù)B剛寫入10條age=20的記錄,而寫操作是不受MVCC影響,能看到最新數(shù)據(jù)的,所以更新成功,而一旦操作成功,這些被操作的數(shù)據(jù)就會對當(dāng)前事務(wù)可見
3. select count(1) from user where age=20;
-- return 10: 出現(xiàn)幻讀
REPEATABLE READ級別,可以防止大部分的幻讀,但像前邊舉例讀-寫-讀的情況,使用不加鎖的select依然會幻讀。
SERIALISABLE
大殺器,該級別下,會自動(dòng)將所有普通select轉(zhuǎn)化為select ... lock in share mode執(zhí)行,即針對同一數(shù)據(jù)的所有讀寫都變成互斥的了,可靠性大大提高,并發(fā)性大大降低。
讀-寫,寫-寫均互斥。
4)總結(jié):幾類讀異常
讀-寫-讀,引起的異常
臟讀:讀取了臟數(shù)據(jù)(不存在的數(shù)據(jù))。 事務(wù)一讀 事務(wù)二寫(未提交) 事務(wù)二讀(臟數(shù)據(jù)) 事務(wù)二回滾
不可重復(fù)讀:既可以讀取修改的數(shù)據(jù),也可以讀取新增的數(shù)據(jù)(幻讀)。 事務(wù)一讀 事務(wù)二寫(更新已提交) 事務(wù)二讀(數(shù)據(jù)不一致,不可重復(fù)讀)
幻讀:僅可以讀取新增的數(shù)據(jù),但是無法讀取修改的數(shù)據(jù); 事務(wù)一讀 事務(wù)二寫(新增已提交) 事務(wù)二讀(數(shù)據(jù)不一致,幻讀)
附命令
查看表的加鎖情況: select * from information_schema.INNODB_LOCKS; 事務(wù)狀態(tài) select * from information_schema.INNODB_TRX;?
來源:https://blog.csdn.net/weixin_28894423/article/details/113493799
總結(jié)
以上是生活随笔為你收集整理的mysql隔离级别底层实现_1、深入理解mysql四种隔离级别及底层实现原理(MVCC和锁)...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: excel表格求和出来是0(excel自
- 下一篇: laravel框架应用和composer