首先說(shuō)一下ACID四大特性:
四大特性
原子性
事務(wù)必須是原子工作單元昙读;對(duì)于其數(shù)據(jù)修改大脉,要么全都執(zhí)行,要么全都不執(zhí)行敬飒。通常痕鳍,與某個(gè)事務(wù)關(guān)聯(lián)的操作具有共同的目標(biāo)硫豆,并且是相互依賴的。如果系統(tǒng)只執(zhí)行這些操作的一個(gè)子集,則可能會(huì)破壞事務(wù)的總體目標(biāo)够庙。原子性消除了系統(tǒng)處理操作子集的可能性恭应。一致性
事務(wù)在完成時(shí),必須使所有的數(shù)據(jù)都保持一致?tīng)顟B(tài)耘眨。在相關(guān)數(shù)據(jù)庫(kù)中,所有規(guī)則都必須應(yīng)用于事務(wù)的修改境肾,以保持所有數(shù)據(jù)的完整性剔难。事務(wù)結(jié)束時(shí),所有的內(nèi)部數(shù)據(jù)結(jié)構(gòu)(如 B 樹(shù)索引或雙向鏈表)都必須是正確的奥喻。某些維護(hù)一致性的責(zé)任由應(yīng)用程序開(kāi)發(fā)人員承擔(dān)偶宫,他們必須確保應(yīng)用程序已強(qiáng)制所有已知的完整性約束。例如环鲤,當(dāng)開(kāi)發(fā)用于轉(zhuǎn)帳的應(yīng)用程序時(shí)纯趋,應(yīng)避免在轉(zhuǎn)帳過(guò)程中任意移動(dòng)小數(shù)點(diǎn)。隔離性
由并發(fā)事務(wù)所作的修改必須與任何其它并發(fā)事務(wù)所作的修改隔離冷离。事務(wù)查看數(shù)據(jù)時(shí)數(shù)據(jù)所處的狀態(tài)吵冒,要么是另一并發(fā)事務(wù)修改它之前的狀態(tài),要么是另一事務(wù)修改它之后的狀態(tài)西剥,事務(wù)不會(huì)查看中間狀態(tài)的數(shù)據(jù)痹栖。這稱為可串行性,因?yàn)樗軌蛑匦卵b載起始數(shù)據(jù)瞭空,并且重播一系列事務(wù)揪阿,以使數(shù)據(jù)結(jié)束時(shí)的狀態(tài)與原始事務(wù)執(zhí)行的狀態(tài)相同。當(dāng)事務(wù)可序列化時(shí)將獲得最高的隔離級(jí)別咆畏。在此級(jí)別上南捂,從一組可并行執(zhí)行的事務(wù)獲得的結(jié)果與通過(guò)連續(xù)運(yùn)行每個(gè)事務(wù)所獲得的結(jié)果相同。由于高度隔離會(huì)限制可并行執(zhí)行的事務(wù)數(shù)旧找,所以一些應(yīng)用程序降低隔離級(jí)別以換取更大的吞吐量溺健。持久性
事務(wù)完成之后,它對(duì)于系統(tǒng)的影響是永久性的钦讳。該修改即使出現(xiàn)致命的系統(tǒng)故障也將一直保持矿瘦。
主要說(shuō)說(shuō)這個(gè)描述很抽象一致性,舉個(gè)例子:A向B轉(zhuǎn)賬愿卒,假設(shè)轉(zhuǎn)賬之前這兩個(gè)用戶的錢(qián)加起來(lái)總共是2000缚去,那么A向B轉(zhuǎn)賬之后,不管這兩個(gè)賬戶怎么轉(zhuǎn)琼开,A用戶的錢(qián)和B用戶的錢(qián)加起來(lái)的總額還是2000易结,這個(gè)就是事務(wù)的一致性。
四種隔離級(jí)別
Read Uncommitted(讀取未提交內(nèi)容)
在該隔離級(jí)別,所有事務(wù)都可以看到其他未提交事務(wù)的執(zhí)行結(jié)果搞动。本隔離級(jí)別很少用于實(shí)際應(yīng)用躏精,因?yàn)樗男阅芤膊槐绕渌?jí)別好多少。讀取未提交的數(shù)據(jù)鹦肿,也被稱之為臟讀(Dirty Read)矗烛。Read Committed(讀取提交內(nèi)容)
這是大多數(shù)數(shù)據(jù)庫(kù)系統(tǒng)的默認(rèn)隔離級(jí)別(但不是MySQL默認(rèn)的)。它滿足了隔離的簡(jiǎn)單定義:一個(gè)事務(wù)只能看見(jiàn)已經(jīng)提交事務(wù)所做的改變箩溃。這種隔離級(jí)別 也支持所謂的不可重復(fù)讀(Nonrepeatable Read)瞭吃,因?yàn)橥皇聞?wù)的其他實(shí)例在該實(shí)例處理其間可能會(huì)有新的commit,所以同一select可能返回不同結(jié)果涣旨。Repeatable Read(可重讀)
這是MySQL的默認(rèn)事務(wù)隔離級(jí)別歪架,它確保同一事務(wù)的多個(gè)實(shí)例在并發(fā)讀取數(shù)據(jù)時(shí),會(huì)看到同樣的數(shù)據(jù)行霹陡。不過(guò)理論上和蚪,這會(huì)導(dǎo)致另一個(gè)棘手的問(wèn)題:幻讀 (Phantom Read)。簡(jiǎn)單的說(shuō)烹棉,幻讀指當(dāng)用戶讀取某一范圍的數(shù)據(jù)行時(shí)攒霹,另一個(gè)事務(wù)又在該范圍內(nèi)插入了新行,當(dāng)用戶再讀取該范圍的數(shù)據(jù)行時(shí)峦耘,會(huì)發(fā)現(xiàn)有新的“幻影” 行剔蹋。InnoDB和Falcon存儲(chǔ)引擎通過(guò)多版本并發(fā)控制(MVCC,Multiversion Concurrency Control)機(jī)制解決了該問(wèn)題辅髓。Serializable(可串行化)
這是最高的隔離級(jí)別泣崩,它通過(guò)強(qiáng)制事務(wù)排序,使之不可能相互沖突洛口,從而解決幻讀問(wèn)題矫付。簡(jiǎn)言之,它是在每個(gè)讀的數(shù)據(jù)行上加上共享鎖第焰。在這個(gè)級(jí)別买优,可能導(dǎo)致大量的超時(shí)現(xiàn)象和鎖競(jìng)爭(zhēng)。這四種隔離級(jí)別采取不同的鎖類型來(lái)實(shí)現(xiàn)挺举,若讀取的是同一個(gè)數(shù)據(jù)的話杀赢,就容易發(fā)生問(wèn)題。例如:
- 臟讀(Drity Read):某個(gè)事務(wù)已更新一份數(shù)據(jù)湘纵,另一個(gè)事務(wù)在此時(shí)讀取了同一份數(shù)據(jù)脂崔,由于某些原因,前一個(gè)RollBack了操作梧喷,則后一個(gè)事務(wù)所讀取的數(shù)據(jù)就會(huì)是不正確的砌左。
- 不可重復(fù)讀(Non-repeatable read):在一個(gè)事務(wù)的兩次查詢之中數(shù)據(jù)不一致脖咐,這可能是兩次查詢過(guò)程中間插入了一個(gè)事務(wù)更新的原有的數(shù)據(jù)。
- 幻讀(Phantom Read):在一個(gè)事務(wù)的兩次查詢中數(shù)據(jù)筆數(shù)不一致汇歹,例如有一個(gè)事務(wù)查詢了幾列(Row)數(shù)據(jù)屁擅,而另一個(gè)事務(wù)卻在此時(shí)插入了新的幾列數(shù)據(jù),先前的事務(wù)在接下來(lái)的查詢中产弹,就會(huì)發(fā)現(xiàn)有幾列數(shù)據(jù)是它先前所沒(méi)有的派歌。
- 在MySQL中,實(shí)現(xiàn)了這四種隔離級(jí)別取视,分別有可能產(chǎn)生問(wèn)題如下所示:
[圖片上傳失敗...(image-65048f-1543570309304)]
然后是關(guān)于數(shù)據(jù)庫(kù)的各種鎖的總結(jié)
共享鎖(又稱讀鎖)硝皂、排它鎖(又稱寫(xiě)鎖)
InnoDB引擎的鎖機(jī)制:InnoDB支持事務(wù),支持行鎖和表鎖用的比較多作谭,Myisam不支持事務(wù),只支持表鎖奄毡。
共享鎖(S):允許一個(gè)事務(wù)去讀一行折欠,阻止其他事務(wù)獲得相同數(shù)據(jù)集的排他鎖。
排他鎖(X):允許獲得排他鎖的事務(wù)更新數(shù)據(jù)吼过,阻止其他事務(wù)取得相同數(shù)據(jù)集的共享讀鎖和排他寫(xiě)鎖锐秦。
意向共享鎖(IS):事務(wù)打算給數(shù)據(jù)行加行共享鎖,事務(wù)在給一個(gè)數(shù)據(jù)行加共享鎖前必須先取得該表的IS鎖盗忱。
意向排他鎖(IX):事務(wù)打算給數(shù)據(jù)行加行排他鎖酱床,事務(wù)在給一個(gè)數(shù)據(jù)行加排他鎖前必須先取得該表的IX鎖
說(shuō)明:
1)共享鎖和排他鎖都是行鎖,意向鎖都是表鎖趟佃,應(yīng)用中我們只會(huì)使用到共享鎖和排他鎖扇谣,意向鎖是mysql內(nèi)部使用的,不需要用戶干預(yù)闲昭。
2)對(duì)于UPDATE罐寨、DELETE和INSERT語(yǔ)句,InnoDB會(huì)自動(dòng)給涉及數(shù)據(jù)集加排他鎖(X)序矩;對(duì)于普通SELECT語(yǔ)句鸯绿,InnoDB不會(huì)加任何鎖,事務(wù)可以通過(guò)以下語(yǔ)句顯示給記錄集加共享鎖或排他鎖簸淀。
共享鎖(S):SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE瓶蝴。
排他鎖(X):SELECT * FROM table_name WHERE ... FOR UPDATE。
對(duì)于鎖定行記錄后需要進(jìn)行更新操作的應(yīng)用租幕,應(yīng)該使用Select...For update 方式舷手,獲取排它鎖。(用共享鎖令蛉,在讀了之后再寫(xiě)會(huì)阻塞聚霜,會(huì)導(dǎo)致死鎖)
這里說(shuō)說(shuō)Myisam:MyISAM在執(zhí)行查詢語(yǔ)句(SELECT)前狡恬,會(huì)自動(dòng)給涉及的所有表加讀鎖,在執(zhí)行更新操作(UPDATE蝎宇、DELETE弟劲、INSERT等)前,會(huì)自動(dòng)給涉及的表加寫(xiě)鎖姥芥。
3)InnoDB行鎖是通過(guò)給索引上的索引項(xiàng)加鎖來(lái)實(shí)現(xiàn)的兔乞,因此InnoDB這種行鎖實(shí)現(xiàn)特點(diǎn)意味著:只有通過(guò)索引條件檢索數(shù)據(jù),InnoDB才使用行級(jí)鎖凉唐,否則庸追,InnoDB將使用表鎖!樂(lè)觀鎖台囱、悲觀鎖
悲觀鎖:悲觀鎖淡溯,正如其名,它指的是對(duì)數(shù)據(jù)被外界(包括本系統(tǒng)當(dāng)前的其他事務(wù)簿训,以及來(lái)自外部系統(tǒng)的事務(wù)處理)修改持保守態(tài)度咱娶,因此,在整個(gè)數(shù)據(jù)處理過(guò)程中强品,將數(shù)據(jù)處于鎖定狀態(tài)膘侮。悲觀鎖的實(shí)現(xiàn),往往依靠數(shù)據(jù)庫(kù)提供的鎖機(jī)制(也只有數(shù)據(jù)庫(kù)層提供的鎖機(jī)制才能真正保證數(shù)據(jù)訪問(wèn)的排他性的榛,否則琼了,即使在本系統(tǒng)中實(shí)現(xiàn)了加鎖機(jī)制,也無(wú)法保證外部系統(tǒng)不會(huì)修改數(shù)據(jù))
1)使用悲觀鎖夫晌,我們必須關(guān)閉mysql數(shù)據(jù)庫(kù)的自動(dòng)提交屬性雕薪,采用手動(dòng)提交事務(wù)的方式,因?yàn)镸ySQL默認(rèn)使用autocommit模式慷丽,也就是說(shuō)蹦哼,當(dāng)你執(zhí)行一個(gè)更新操作后,MySQL會(huì)立刻將結(jié)果進(jìn)行提交要糊。
2)需要注意的是纲熏,在事務(wù)中,只有SELECT ... FOR UPDATE 或LOCK IN SHARE MODE 同一筆數(shù)據(jù)時(shí)會(huì)等待其它事務(wù)結(jié)束后才執(zhí)行锄俄,一般SELECT ... 則不受此影響局劲。對(duì)于UPDATE、DELETE和INSERT語(yǔ)句奶赠,InnoDB會(huì)自動(dòng)給涉及數(shù)據(jù)集加排他鎖(X)鱼填。
3)補(bǔ)充:MySQL select…for update的Row Lock與Table Lock
使用select…for update會(huì)把數(shù)據(jù)給鎖住,不過(guò)我們需要注意一些鎖的級(jí)別毅戈,MySQL InnoDB默認(rèn)Row-Level Lock苹丸,所以只有「明確」地指定主鍵(或有索引的地方)愤惰,MySQL 才會(huì)執(zhí)行Row lock (只鎖住被選取的數(shù)據(jù)) ,否則MySQL 將會(huì)執(zhí)行Table Lock (將整個(gè)數(shù)據(jù)表單給鎖住)赘理。
樂(lè)觀鎖:樂(lè)觀鎖( Optimistic Locking ) 相對(duì)悲觀鎖而言宦言,樂(lè)觀鎖假設(shè)認(rèn)為數(shù)據(jù)一般情況下不會(huì)造成沖突,所以在數(shù)據(jù)進(jìn)行提交更新的時(shí)候商模,才會(huì)正式對(duì)數(shù)據(jù)的沖突與否進(jìn)行檢測(cè)奠旺,如果發(fā)現(xiàn)沖突了,則讓返回用戶錯(cuò)誤的信息施流,讓用戶決定如何去做(一般是回滾事務(wù))响疚。那么我們?nèi)绾螌?shí)現(xiàn)樂(lè)觀鎖呢,一般來(lái)說(shuō)有以下2種方式:
1).使用數(shù)據(jù)版本(Version)記錄機(jī)制實(shí)現(xiàn)瞪醋,這是樂(lè)觀鎖最常用的一種實(shí)現(xiàn)方式忿晕。何謂數(shù)據(jù)版本?即為數(shù)據(jù)增加一個(gè)版本標(biāo)識(shí)银受,一般是通過(guò)為數(shù)據(jù)庫(kù)表增加一個(gè)數(shù)字類型的 “version” 字段來(lái)實(shí)現(xiàn)杏糙。當(dāng)讀取數(shù)據(jù)時(shí),將version字段的值一同讀出蚓土,數(shù)據(jù)每更新一次,對(duì)此version值加一赖淤。當(dāng)我們提交更新的時(shí)候蜀漆,判斷數(shù)據(jù)庫(kù)表對(duì)應(yīng)記錄的當(dāng)前版本信息與第一次取出來(lái)的version值進(jìn)行比對(duì),如果數(shù)據(jù)庫(kù)表當(dāng)前版本號(hào)與第一次取出來(lái)的version值相等咱旱,則予以更新确丢,否則認(rèn)為是過(guò)期數(shù)據(jù)。
2).樂(lè)觀鎖定的第二種實(shí)現(xiàn)方式和第一種差不多吐限,同樣是在需要樂(lè)觀鎖控制的table中增加一個(gè)字段鲜侥,名稱無(wú)所謂,字段類型使用時(shí)間戳(timestamp), 和上面的version類似诸典,也是在更新提交的時(shí)候檢查當(dāng)前數(shù)據(jù)庫(kù)中數(shù)據(jù)的時(shí)間戳和自己更新前取到的時(shí)間戳進(jìn)行對(duì)比描函,如果一致則OK,否則就是版本沖突狐粱。
總結(jié):兩種鎖各有優(yōu)缺點(diǎn)舀寓,不可認(rèn)為一種好于另一種,像樂(lè)觀鎖適用于寫(xiě)比較少的情況下肌蜻,即沖突真的很少發(fā)生的時(shí)候互墓,這樣可以省去了鎖的開(kāi)銷,加大了系統(tǒng)的整個(gè)吞吐量蒋搜。但如果經(jīng)常產(chǎn)生沖突篡撵,上層應(yīng)用會(huì)不斷的進(jìn)行retry判莉,這樣反倒是降低了性能,所以這種情況下用悲觀鎖就比較合適育谬。
另外券盅,高并發(fā)情況下個(gè)人認(rèn)為樂(lè)觀鎖要好于悲觀鎖,因?yàn)楸^鎖的機(jī)制使得各個(gè)線程等待時(shí)間過(guò)長(zhǎng)斑司,極其影響效率渗饮,樂(lè)觀鎖可以在一定程度上提高并發(fā)度。表鎖宿刮、行鎖
表級(jí)鎖(table-level locking):MyISAM和MEMORY存儲(chǔ)引擎
行級(jí)鎖(row-level locking) :InnoDB存儲(chǔ)引擎
頁(yè)面鎖(page-level-locking):BDB存儲(chǔ)引擎
表級(jí)鎖:開(kāi)銷小互站,加鎖快;不會(huì)出現(xiàn)死鎖;鎖定粒度大,發(fā)生鎖沖突的概率最高,并發(fā)度最低僵缺。
行級(jí)鎖:開(kāi)銷大胡桃,加鎖慢;會(huì)出現(xiàn)死鎖;鎖定粒度最小,發(fā)生鎖沖突的概率最低,并發(fā)度也最高磕潮。
頁(yè)面鎖:開(kāi)銷和加鎖時(shí)間界于表鎖和行鎖之間;會(huì)出現(xiàn)死鎖;鎖定粒度界于表鎖和行鎖之間翠胰,并發(fā)度一般。