一、事務的四大特性(ACID)
如果一個數(shù)據(jù)庫聲稱支持事務的操作,那么該數(shù)據(jù)庫必須要具備以下四個特性:
1.1 原子性(Atomicity)
原子性是指事務包含的所有操作要么全部成功俐筋,要么全部失敗回滾知纷,因此事務的操作如果成功就必須要完全應用到數(shù)據(jù)庫,如果操作失敗則不能對數(shù)據(jù)庫有任何影響。
1.2 一致性(Consistency)
一致性是指事務必須使數(shù)據(jù)庫從一個一致性狀態(tài)變換到另一個一致性狀態(tài)互站,也就是說一個事務執(zhí)行之前和執(zhí)行之后都必須處于一致性狀態(tài)。拿轉(zhuǎn)賬來說僵缺,假設用戶A和用戶B兩者的錢加起來一共是5000胡桃,那么不管A和B之間如何轉(zhuǎn)賬,轉(zhuǎn)幾次賬磕潮,事務結(jié)束后兩個用戶的錢相加起來應該還得是5000翠胰,這就是事務的一致性。
1.3 隔離性(Isolation)
隔離性是當多個用戶并發(fā)訪問數(shù)據(jù)庫時自脯,比如操作同一張表時之景,數(shù)據(jù)庫為每一個用戶開啟的事務,不能被其他事務的操作所干擾膏潮,多個并發(fā)事務之間要相互隔離锻狗。即要達到這么一種效果:對于任意兩個并發(fā)的事務T1和T2,在事務T1看來焕参,T2要么在T1開始之前就已經(jīng)結(jié)束轻纪,要么在T1結(jié)束之后才開始,這樣每個事務都感覺不到有其他事務在并發(fā)地執(zhí)行龟糕。
1.4 持久性(Durability)
持久性是指一個事務一旦被提交了桐磁,那么對數(shù)據(jù)庫中的數(shù)據(jù)的改變就是永久性的悔耘,即便是在數(shù)據(jù)庫系統(tǒng)遇到故障的情況下也不會丟失提交事務的操作讲岁。例如我們在使用JDBC操作數(shù)據(jù)庫時,在提交事務方法后衬以,提示用戶事務操作完成缓艳,當我們程序執(zhí)行完成直到看到提示后,就可以認定事務以及正確提交看峻,即使這時候數(shù)據(jù)庫出現(xiàn)了問題阶淘,也必須要將我們的事務完全執(zhí)行完成,否則就會造成我們看到提示事務處理完畢互妓,但是數(shù)據(jù)庫因為故障而沒有執(zhí)行事務的重大錯誤溪窒。
二、數(shù)據(jù)庫隔離級別
數(shù)據(jù)庫事務的隔離級別有4個冯勉。由低到高依次為Read uncommitted澈蚌、Read committed、Repeatable read灼狰、Serializable宛瞄。這四個級別能夠逐個解決臟讀、不可反復讀交胚、幻讀這幾類問題份汗。MySQL設置的隔離級別默認是Repeatable Read可反復讀級別
注意:MySQL 中默認的事務隔離級別就是 Repeatable Read可反復讀級別盈电,但是它通過 Next-Key 鎖也能夠在某種程度上解決幻讀的問題。
事務級別 | 臟讀 | 不可反復讀 | 幻讀 |
---|---|---|---|
Read uncommitted | √ | √ | √ |
Read committed | × | √ | √ |
Repeatable read | × | × | √ |
Serializable | × | × | × |
√: 可能出現(xiàn) ×: 不會出現(xiàn)
注意:我們討論隔離級別的場景杯活,主要是在多個事務并發(fā)的情況下匆帚。因此,接下來的解說都環(huán)繞事務并發(fā)旁钧。
2.1 Read uncommitted 讀未提交
Read uncommitted是限制性最弱的隔離級別卷扮。由于該級別忽略其它事務放置的鎖。使用READ UNCOMMITTED級別運行的事務均践,能夠讀取尚未由其它事務提交的改動后的數(shù)據(jù)值晤锹,這些行為稱為“臟”讀。我們所說的臟讀彤委,兩個并發(fā)的事務鞭铆,“事務A:領導給singo發(fā)工資”、“事務B:singo查詢工資賬戶”焦影,事務B讀取了事務A尚未提交的數(shù)據(jù)车遂。比方,事務1改動一行斯辰,事務2在事務1提交之前讀取了這一行舶担。假設事務1回滾,事務2就讀取了一行沒有提交的數(shù)據(jù)彬呻。這種數(shù)據(jù)我們覺得是不存在的衣陶。
補充:
比如事務B讀取了事務A 更改但未提交的數(shù)據(jù)D。有人會問闸氮,這會有什么問題嗎剪况?如果事務A沒有再修改數(shù)據(jù)D,并且事務也沒有出錯正常提交了蒲跨,其實事務B讀取了數(shù)據(jù)D沒有什么問題译断。但如果事務B讀取數(shù)據(jù)D后,事務A又改了數(shù)據(jù)D呢或悲?甚至如果事務A失敗回滾了呢孙咪?那么事務B讀的數(shù)據(jù)D就是錯的值,如果事務B對這個數(shù)據(jù)D再做修改巡语,肯定就是錯的翎蹈。
2.2 Read committed 讀提交
該級別通過指定語句不能讀取其它事務已改動可是尚未提交的數(shù)據(jù)值。禁止運行臟讀捌臊。在當前事務中的各個語句運行之間杨蛋,其它事務仍能夠改動、插入或刪除數(shù)據(jù)。從而產(chǎn)生無法反復的讀操作逞力∈锕眩或“影子”數(shù)據(jù)。比方寇荧,事務1讀取了一行举庶。事務2改動或者刪除這一行而且提交。假設事務1想再一次讀取這一行揩抡,它將獲得改動后的數(shù)據(jù)或者發(fā)現(xiàn)這一樣已經(jīng)被刪除户侥。因此事務的第二次讀取結(jié)果與第一次讀取結(jié)果不同,因此也叫不可反復讀峦嗤。
大多數(shù)數(shù)據(jù)庫的默認級別就是Read committed蕊唐。比方Sql Server , Oracle。怎樣解決不可反復讀這一問題烁设。請看下一個隔離級別替梨。
2.3 Repeatable read 反復讀
Repeatable read是比Read committed限制性更強的隔離級別。
該級別包含Read committed装黑,而且另外指定了在當前事務提交之前副瀑。其它不論什么事務均不能夠改動或刪除當前事務已讀取的數(shù)據(jù)。并發(fā)性低于Read committed恋谭。由于已讀數(shù)據(jù)的共享鎖在整個事務期間持有糠睡,而不是在每一個語句結(jié)束時釋放。
這個隔離級別僅僅是說疚颊,不可以改動和刪除狈孔,可是并沒有強制不能插入新的滿足條件查詢的數(shù)據(jù)行。此可以得出結(jié)論:Read committed隔離級別保證了在同樣的查詢條件下串稀,同一個事務中的兩個查詢除抛。第二次讀取的內(nèi)容肯定包含第一次讀到的內(nèi)容。
反復讀與幻讀
反復讀是為了保證在一個事務中母截,相同查詢條件下讀取的數(shù)據(jù)值不發(fā)生改變,可是不能保證下次相同條件查詢橄教。結(jié)果記錄數(shù)不會添加清寇。
幻讀就是為了解決問題而存在的,他將這個查詢范圍都加鎖了护蝶。所以就不能再往這個范圍內(nèi)插入數(shù)據(jù)华烟。這就是SERIALIZABLE 隔離級別做的事情。
2.4 Serializable 序列化
Serializable 是限制性最強的隔離級別持灰,由于該級別鎖定整個范圍的鍵盔夜。并一直持有鎖,直到事務完畢。該級別包含REPEATABLE READ喂链。并添加了在事務完畢之前返十,其它事務不能向事務已讀取的范圍插入新行的限制。比方椭微,事務1讀取了一系列滿足搜索條件的行洞坑。事務2在運行SQL statement產(chǎn)生一行或者多行滿足事務1搜索條件的行時會沖突。則事務2回滾蝇率。這時事務1再次讀取了一系列滿足同樣搜索條件的行迟杂。第二次讀取的結(jié)果和第一次讀取的結(jié)果同樣。
三本慕、鎖
3.1 一次封鎖or兩段鎖排拷?
由于有大量的并發(fā)訪問,為了預防死鎖锅尘。一般應用中推薦使用一次封鎖法攻泼。就是在方法的開始階段。已經(jīng)預先知道會用到哪些數(shù)據(jù)鉴象,然后所有鎖住忙菠,在方法執(zhí)行之后,再所有解鎖纺弊。
這樣的方式能夠有效的避免循環(huán)死鎖牛欢,但在數(shù)據(jù)庫中卻不適用,由于在事務開始階段淆游,數(shù)據(jù)庫并不知道會用到哪些數(shù)據(jù)傍睹。數(shù)據(jù)庫遵循的是兩段鎖協(xié)議,將事務分成兩個階段犹菱,加鎖階段和解鎖階段(所以叫兩段鎖)
加鎖階段:在該階段能夠進行加鎖操作拾稳。在對不論什么數(shù)據(jù)進行讀操作之前要申請并獲得S鎖(共享鎖,其他事務能夠繼續(xù)加共享鎖腊脱,但不能加排它鎖)访得,在進行寫操作之前要申請并獲得X鎖(排它鎖,其他事務不能再獲得不論什么鎖)陕凹。加鎖不成功悍抑,則事務進入等待狀態(tài),直到加鎖成功才繼續(xù)運行杜耙。
解鎖階段:當事務釋放了一個封鎖以后搜骡,事務進入解鎖階段。在該階段僅僅能進行解鎖操作不能再進行加鎖操作佑女。
事務 加鎖/解鎖處理
begin记靡。
insert into test ..... 加insert相應的鎖
update test set... 加update相應的鎖
delete from test .... 加delete相應的鎖
commit; 事務提交時谈竿,同一時候釋放insert、update摸吠、delete相應的鎖
這樣的方式盡管無法避免死鎖空凸。可是兩段鎖協(xié)議能夠保證事務的并發(fā)調(diào)度是串行化(串行化非常重要蜕便,尤其是在數(shù)據(jù)恢復和備份的時候)的劫恒。
3.2不可反復讀和幻讀的差別
非常多人easy搞混不可反復讀和幻讀,確實這兩者有些相似轿腺。但不可反復讀重點在于update和delete两嘴。而幻讀的重點在于insert。
假設使用鎖機制來實現(xiàn)這兩種隔離級別族壳。在可反復讀中,該sql第一次讀取到數(shù)據(jù)后贰您。就將這些數(shù)據(jù)加鎖锦亦,其他事務無法改動這些數(shù)據(jù)杠园。就能夠?qū)崿F(xiàn)可反復讀了抛蚁。但這樣的方法卻無法鎖住insert的數(shù)據(jù)瞧甩。所以當事務A先前讀取了數(shù)據(jù)肚逸,或者改動了所有數(shù)據(jù)吼虎,事務B還是能夠insert數(shù)據(jù)提交苍鲜,這時事務A就會發(fā)現(xiàn)莫名其妙多了一條之前沒有的數(shù)據(jù)混滔。這就是幻讀坯屿。不能通過行鎖來避免领跛。須要Serializable隔離級別 吠昭。讀用讀鎖矢棚,寫用寫鎖蘑拯,讀鎖和寫鎖相互排斥申窘,這么做能夠有效的避免幻讀剃法、不可反復讀、臟讀等問題恩脂,但會極大的減少數(shù)據(jù)庫的并發(fā)能力俩块。
所以說不可反復讀和幻讀最大的差別浓领,就在于怎樣通過鎖機制來解決他們產(chǎn)生的問題玉凯。
上文說的,是使用悲觀鎖機制來處理這兩種問題联贩,可是MySQL漫仆、ORACLE、PostgreSQL等成熟的數(shù)據(jù)庫泪幌。出于性能考慮盲厌,都是使用了以樂觀鎖為理論基礎的MVCC(多版本號并發(fā)控制)來避免這兩種問題署照。
3.3 悲觀鎖和樂觀鎖
悲觀鎖
正如其名,它指的是對數(shù)據(jù)被外界(包含本系統(tǒng)當前的其它事務吗浩,以及來自外部系統(tǒng)的事務處理)改動持保守態(tài)度建芙。因此,在整個數(shù)據(jù)處理過程中懂扼,將數(shù)據(jù)處于鎖定狀態(tài)禁荸。悲觀鎖的實現(xiàn)。往往依靠數(shù)據(jù)庫提供的鎖機制(也僅僅有數(shù)據(jù)庫層提供的鎖機制才干真正保證數(shù)據(jù)訪問的排他性阀湿,否則赶熟,即使在本系統(tǒng)中實現(xiàn)了加鎖機制,也無法保證外部系統(tǒng)不會改動數(shù)據(jù))炕倘。在悲觀鎖的情況下钧大,為了保證事務的隔離性,就須要一致性鎖定讀罩旋。讀取數(shù)據(jù)時給加鎖啊央,其他事務無法改動這些數(shù)據(jù)。改動刪除數(shù)據(jù)時也要加鎖涨醋,其他事務無法讀取這些數(shù)據(jù)瓜饥。
樂觀鎖
相對悲觀鎖而言,樂觀鎖機制採取了更加寬松的加鎖機制浴骂。悲觀鎖大多數(shù)情況下依靠數(shù)據(jù)庫的鎖機制實現(xiàn)乓土,以保證操作最大程度的獨占性。但隨之而來的就是數(shù)據(jù)庫性能的大量開銷溯警。特別是對長事務而言趣苏,這種開銷往往無法承受。而樂觀鎖機制在一定程度上攻克了這個問題梯轻。樂觀鎖食磕,大多是基于數(shù)據(jù)版本號( Version )記錄機制實現(xiàn)。何謂數(shù)據(jù)版本號喳挑?即為數(shù)據(jù)添加一個版本號標識彬伦,在基于數(shù)據(jù)庫表的版本號解決方式中,通常是通過為數(shù)據(jù)庫表添加一個“version” 字段來實現(xiàn)伊诵。讀取出數(shù)據(jù)時单绑,將此版本號號一同讀出,之后更新時曹宴,對此版本號號加一搂橙。
此時。將提交數(shù)據(jù)的版本號數(shù)據(jù)與數(shù)據(jù)庫表相應記錄的當前版本號信息進行比對笛坦,假設提交的數(shù)據(jù)版本號號大于數(shù)據(jù)庫表當前版本號號份氧,則予以更新唯袄。否則覺得是過期數(shù)據(jù)弯屈。
要說明的是蜗帜,MVCC的實現(xiàn)沒有固定的規(guī)范,每一個數(shù)據(jù)庫都會有不同的實現(xiàn)方式资厉,這里討論的是InnoDB的MVCC厅缺。
四、MVCC在MySQL的InnoDB中的實現(xiàn)
在InnoDB中宴偿,會在每行數(shù)據(jù)后加入兩個額外的隱藏的值來實現(xiàn)MVCC湘捎,這兩個值一個記錄這行數(shù)據(jù)何時被創(chuàng)建,另外一個記錄這行數(shù)據(jù)何時過期(或者被刪除)窄刘。 在實際操作中窥妇。存儲的并非時間,而是事務的版本娩践,每開啟一個新事務活翩,事務的版本就會遞增。 在可重讀Repeatable reads事務隔離級別下:
SELECT時翻伺。讀取創(chuàng)建版本<=當前事務版本材泄。刪除版本為空或>當前事務版本。
INSERT時吨岭,保存當前事務版本為行的創(chuàng)建版本
DELETE時拉宗,保存當前事務版本為行的刪除版本
UPDATE時,插入一條新紀錄辣辫。保存當前事務版本為行創(chuàng)建版本旦事,同一時候保存當前事務版本到原來刪除的行
通過MVCC,盡管每行記錄都須要額外的存儲空間急灭,很多其它的行檢查工作以及一些額外的維護工作姐浮。但能夠降低鎖的使用,大多數(shù)讀操作都不用加鎖化戳,讀數(shù)據(jù)操作非常easy单料,性能非常好,而且也能保證僅僅會讀取到符合標準的行点楼。也僅僅鎖住必要行扫尖。
我們無論從數(shù)據(jù)庫方面的教課書中學到。還是從網(wǎng)絡上看到掠廓,大都是上文中事務的四種隔離級別這一模塊列出的意思换怖,RR級別是可反復讀的,但無法解決幻讀蟀瞧。而僅僅有在Serializable級別才干解決幻讀沉颂。
轉(zhuǎn)自:MySql的隔離級別和鎖的關系