MySQL事務(wù)處理特性的實(shí)現(xiàn)原理
?
本文分享自華為云社區(qū)《【數(shù)據(jù)庫事務(wù)與鎖機(jī)制】- 事務(wù)的隔離級(jí)別》,原文作者:技術(shù)火炬手?。 ? ? ? ? ? ? ? ? ?
摘要:事務(wù)這個(gè)詞來自于英語中的transactional這個(gè)詞的翻譯,這個(gè)詞的含義更多的是指 “交易”。在數(shù)據(jù)庫系統(tǒng)或者軟件系統(tǒng)中我們通常 稱 transactional 為事務(wù)
數(shù)據(jù)庫事務(wù)的四個(gè)特性 ACID:分別是 原子性、一致性、隔離性、持久性。數(shù)據(jù)庫事務(wù)的這四大特性來源于 ISO標(biāo)準(zhǔn)的 ISO/IEC 10026-1:1992/COR 1:1996,它定義了事務(wù)需要具備以上四個(gè)特性。那么在InnoDB中是如何實(shí)現(xiàn)這些特征的呢?下面內(nèi)容我們討論 MySQL (下指InnoDB引擎)對(duì)事務(wù)特性的支持是怎么實(shí)現(xiàn)的。
討論MySQL的事務(wù)處理特性的實(shí)現(xiàn)原理之前我們需要先了解下MySQL對(duì)MVCC的支持,關(guān)于MVCC 維基百科有如下解釋。
多版本并發(fā)控制(Multiversion concurrency control, MCC 或 MVCC),是數(shù)據(jù)庫管理系統(tǒng)常用的一種并發(fā)控制,也用于程序設(shè)計(jì)語言實(shí)現(xiàn)事務(wù)內(nèi)存。MVCC意圖解決讀寫鎖造成的多個(gè)、長時(shí)間的讀操作餓死寫操作問題。每個(gè)事務(wù)讀到的數(shù)據(jù)項(xiàng)都是一個(gè)歷史快照(snapshot)并依賴于實(shí)現(xiàn)的隔離級(jí)別。寫操作不覆蓋已有數(shù)據(jù)項(xiàng),而是創(chuàng)建一個(gè)新的版本,直至所在操作提交時(shí)才變?yōu)榭梢姟?煺崭綦x使得事物看到它啟動(dòng)時(shí)的數(shù)據(jù)狀態(tài)
數(shù)據(jù)庫事務(wù)的隔離級(jí)別
為了實(shí)現(xiàn)事務(wù)的隔離性,ISO 標(biāo)準(zhǔn)組織對(duì)事務(wù)鎖需要實(shí)現(xiàn)的隔離級(jí)別有四種定義,下面我們先對(duì)四種事務(wù)隔離的級(jí)別簡單闡述一下。
READ UNCOMMITTED?讀未提交
RU(READ UNCOMMITTED)?被稱為讀未提交,有些資料稱之為瀏覽穩(wěn)定(browse access)但是正確的翻譯應(yīng)該是未提交讀。RU是最低標(biāo)準(zhǔn)的隔離,未提交讀的意思就是在事務(wù)并發(fā)的情況下,可以容許一個(gè)事務(wù)在沒有提交修改的的情況下被另外一個(gè)事務(wù)讀取到這個(gè)修改,這就就會(huì)產(chǎn)生臟讀的情況。下面這個(gè)表格是各個(gè)事務(wù)隔離級(jí)別對(duì)于臟讀、幻讀、可重復(fù)讀的抑制情況,事實(shí)上RU不但會(huì)產(chǎn)生臟讀的情況而且其他兩種讀的情況都會(huì)發(fā)生。

首先我們有必要澄清一下以上三種數(shù)據(jù)讀問題的概念,對(duì)于數(shù)據(jù)庫事務(wù)來說我們簡單的認(rèn)識(shí)是一系列的數(shù)據(jù)庫操作在一個(gè)事務(wù)中,這個(gè)事務(wù)要不全部成功要不全部失敗,但是要知道數(shù)據(jù)庫在實(shí)際使用的過程中不是串行的,它是并發(fā)的,串行場景下我們事先事務(wù)就非常簡單了,就是一個(gè)一個(gè)操作嘛,大家排隊(duì)執(zhí)行。但是在并發(fā)事務(wù)的場景下就會(huì)出現(xiàn)對(duì)同一個(gè)數(shù)據(jù)的競爭問題,簡單的理解就是你也要讀寫這個(gè)數(shù)據(jù),我也要讀寫這個(gè)數(shù)據(jù),那么大家多個(gè)事務(wù)操作一個(gè)數(shù)據(jù)的時(shí)候怎么保證數(shù)據(jù)的一致和完整?這個(gè)時(shí)候就會(huì)出現(xiàn)數(shù)據(jù)的臟讀、幻讀、重復(fù)讀問題。
臟讀
當(dāng)一個(gè)事務(wù)允許讀取另外一個(gè)事務(wù)修改但未提交的數(shù)據(jù)時(shí),就可能發(fā)生臟讀(dirty reads)
臟讀是指多個(gè)事務(wù)同時(shí)讀寫一個(gè)數(shù)據(jù),當(dāng)事務(wù)1中修改和讀取數(shù)據(jù)A時(shí),事務(wù)2對(duì)數(shù)據(jù)A做了修改,然后這個(gè)修改反映到了事務(wù)A中。
我們?cè)囅胗羞@樣的場景,假如兩個(gè)事務(wù)都在操作金額表中的同一條記錄,事務(wù)A需要獲得到當(dāng)前金額值然后給他做加3的操作(用于買黃瓜),原來這個(gè)金額的值是5,但是此時(shí)事務(wù)B將這條數(shù)據(jù)的金額修改成了8,然后這個(gè)修改被事務(wù)A拿到然后在8的基礎(chǔ)上加了3等于11。但是萬萬沒想到在A事務(wù)做完這個(gè)操作以后B事務(wù)回滾了(反悔了,香蕉的錢沒給)。這個(gè)時(shí)候A事務(wù)完成以后賬戶的金額莫名其妙的變成了11,但是事實(shí)上應(yīng)該是8。這也就是臟讀的情況。
不可重復(fù)讀
在一次事務(wù)中,當(dāng)一行數(shù)據(jù)獲取兩遍得到不同的結(jié)果表示發(fā)生了不可重復(fù)讀(non-repeatable reads)
在理解不可重復(fù)讀之前先理解什么是可重復(fù)讀,可重復(fù)讀的意思就是在一個(gè)事務(wù)中對(duì)同一個(gè)數(shù)據(jù)的多次讀取其結(jié)果應(yīng)該是相同的(在這個(gè)事務(wù)中沒有修改它的值)。那么反過來的意思就是在一個(gè)事務(wù)中對(duì)一個(gè)數(shù)據(jù)的多次讀取的值是不一樣的,什么情況下會(huì)出現(xiàn)不可重復(fù)讀呢?
還是上面的例子,假如事務(wù)A在做加3操作之前先讀取了原來的值也就是5,然后繼續(xù)其他操作,這個(gè)時(shí)候事務(wù)B對(duì)這條記錄進(jìn)行了加3的操作然后提交了,當(dāng)事務(wù)A再次讀這個(gè)值的時(shí)候發(fā)現(xiàn)當(dāng)前值變成了8,這個(gè)時(shí)候前后兩次的值完全不一樣,這也就是不可重復(fù)讀。
不可重復(fù)讀是針對(duì)單個(gè)事務(wù)來說的,也就是在一個(gè)事務(wù)中是否可以對(duì)一條數(shù)據(jù)做重復(fù)的讀取,如果不能,那么也就意味著不滿足可重復(fù)讀的要求。
不可重復(fù)讀和臟讀非常類似,但是兩者是有區(qū)別的臟讀是指事務(wù)2沒有提交這個(gè)修改就被事務(wù)1獲取到了修改后的值,而不可重復(fù)讀是指提交了修改以后產(chǎn)生了不一致的情況。
幻讀
在事務(wù)執(zhí)行過程中,當(dāng)兩個(gè)完全相同的查詢語句執(zhí)行得到不同的結(jié)果集。這種現(xiàn)象稱為幻讀(phantom read)
幻讀實(shí)際上是不可重讀的一種場景,比如在事務(wù)1中,第一次按照某個(gè)條件讀取到了3條數(shù)據(jù),但是此時(shí)事務(wù)2在這個(gè)表中添加了一條滿足此條件的數(shù)據(jù),在事務(wù)1第二次讀的時(shí)候發(fā)現(xiàn)多了一條數(shù)據(jù)(反過來就是少了一條數(shù)據(jù)),這時(shí)候?qū)τ谑聞?wù)1來說就有點(diǎn)莫名其妙了,貌似產(chǎn)生了幻覺(發(fā)多貨了),所以稱之為幻讀。
所以針對(duì)未提交讀這種隔離級(jí)別,這三種讀問題都有可能產(chǎn)生,所以它是級(jí)別最低的事務(wù)隔離。
READ COMMITTED?讀提交
RC(READ COMMITTED)?讀提交是指在提交以后可以讀,有些資料稱之為提交讀(國內(nèi)翻譯也是醉了)。提交讀主要針對(duì)的場景是UPDATE語句,就是針對(duì)更新只有提交了以后才能讀,試想一下在上面介紹臟讀的時(shí)候,如果事務(wù)2在修改完金額以后提交了這個(gè)值而不是回滾,那么久沒有臟讀的情況。
這也就是為什么提交讀只能解決臟讀的問題而不能解決其他兩種讀的問題。因?yàn)楹茱@然就算事務(wù)2提交了這次修改,那么對(duì)于事務(wù)1來說前后兩次的讀取都是不一致的(不可重復(fù)讀),當(dāng)然幻讀的場景更是存在了,因?yàn)榛米x本來就是不可重復(fù)讀的特殊場景。
REPEATABLE READS?可重復(fù)讀
RR(REPEATABLE READS)可重復(fù)讀是僅次于SERIALIZABLE(串行化)的一種事務(wù)隔離級(jí)別,通常可重復(fù)讀是通過鎖實(shí)現(xiàn)的,它避免不了幻讀的產(chǎn)生。在InnoDB中默認(rèn)采用RR這種事務(wù)隔離級(jí)別,但是和其他數(shù)據(jù)庫不同的是InnoDB在在RR的事務(wù)隔離級(jí)別下采用了NKL的鎖算法(Next-Key Lock),避免了幻讀的產(chǎn)生。這與其他數(shù)據(jù)庫不同,所以在InnoDB中RR的事務(wù)隔離級(jí)別達(dá)到了串行化的事務(wù)隔離標(biāo)準(zhǔn)。
NKL是指鎖定一個(gè)范圍和數(shù)據(jù)本身,而不是只單單鎖定數(shù)據(jù)本身,這樣能夠避免幻讀的產(chǎn)生,官方文檔
SERIALIZABLE?可串行化
是最高級(jí)別的事務(wù)隔離,按照定義是指所有事務(wù)都按照串行化進(jìn)行執(zhí)行,也就是沒有并發(fā)事務(wù)的產(chǎn)生,這樣就避免了所有讀問題,但是這對(duì)于數(shù)據(jù)庫來說是不可能的,因?yàn)槿魏我粋€(gè)數(shù)據(jù)庫都不能忍受這種情況,所以大多數(shù)人認(rèn)為采用這種事務(wù)隔離級(jí)別會(huì)對(duì)性能產(chǎn)生非常大的影響,但是有些論文通過實(shí)驗(yàn)得出串行化并不會(huì)對(duì)性能產(chǎn)生太大的影響。
關(guān)于串行化是不是對(duì)性能產(chǎn)生影響,這取決于數(shù)據(jù)庫對(duì)這種事務(wù)隔離級(jí)別的實(shí)現(xiàn),不能完全說串行就一定慢,反正我是不知道是不是真的對(duì)性能影響很大。
MySQL數(shù)據(jù)庫事務(wù)隔離級(jí)別查詢和修改
查詢事務(wù)隔離級(jí)別
在MySQL中我們可以通過以下方式查詢數(shù)據(jù)庫采用的事務(wù)隔離級(jí)別
show variables like '%tx_isolation%';
??# 查詢回話的事務(wù)隔離級(jí)別
SELECT @@session.tx_isolation;
#查看全局的隔離級(jí)別
SELECT @@global.tx_isolation;
修改事務(wù)隔離級(jí)別
MySQL 提供了 SET TRANSACTION 語句,該語句可以改變單個(gè)會(huì)話或全局的事務(wù)隔離級(jí)別。語法格式如下:
SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}
其中,SESSION 和 GLOBAL 關(guān)鍵字用來指定修改的事務(wù)隔離級(jí)別的范圍
SESSION:
表示修改的事務(wù)隔離級(jí)別將應(yīng)用于當(dāng)前 session(當(dāng)前 cmd 窗口)內(nèi)的所有事務(wù);
GLOBAL:
表示修改的事務(wù)隔離級(jí)別將應(yīng)用于所有 session(全局)中的所有事務(wù),且當(dāng)前已經(jīng)存在的 session 不受影響;
如果省略 SESSION 和 GLOBAL,表示修改的事務(wù)隔離級(jí)別將應(yīng)用于當(dāng)前 session 內(nèi)的下一個(gè)還未開始的事務(wù)。
任何用戶都能改變會(huì)話的事務(wù)隔離級(jí)別,但是只有擁有 SUPER 權(quán)限的用戶才能改變?nèi)值氖聞?wù)隔離級(jí)別
JDBC?修改當(dāng)前連接的隔離級(jí)別
connection.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
end
*版權(quán)聲明:轉(zhuǎn)載文章和圖片均來自公開網(wǎng)絡(luò),版權(quán)歸作者本人所有,推送文章除非無法確認(rèn),我們都會(huì)注明作者和來源。如果出處有誤或侵犯到原作者權(quán)益,請(qǐng)與我們聯(lián)系刪除或授權(quán)事宜。
長按識(shí)別圖中二維碼
關(guān)注獲取更多資訊
不點(diǎn)關(guān)注,我們哪來故事?

點(diǎn)個(gè)再看,你最好看
