【云圖說】第235期 DDS讀寫兩步走 帶您領略只讀節(jié)點的風采
813
2025-03-31
事務這個詞來自于英語中的transactional這個詞的翻譯,這個詞的含義更多的是指 “交易”。在數(shù)據(jù)庫系統(tǒng)或者軟件系統(tǒng)中我們通常 稱 transactional 為事務,
數(shù)據(jù)庫事務的四個特性 ACID。分別是 原子性、一致性、隔離性、持久性。數(shù)據(jù)庫事務的這四大特性來源于 ISO標準的 ISO/IEC 10026-1:1992/COR 1:1996,它定義了事務需要具備以上四個特性。那么在InnoDB中是如何實現(xiàn)這些特征的呢?下面內(nèi)容我們討論 MySQL (下指InnoDB引擎)對事務特性的支持是怎么實現(xiàn)的。
討論MySQL的事務處理特性的實現(xiàn)原理之前我們需要先了解下MySQL對MVCC的支持,關于MVCC?維基百科有如下解釋。
多版本并發(fā)控制(Multiversion concurrency control, MCC 或 MVCC),是數(shù)據(jù)庫管理系統(tǒng)常用的一種并發(fā)控制,也用于程序設計語言實現(xiàn)事務內(nèi)存。MVCC意圖解決讀寫鎖造成的多個、長時間的讀操作餓死寫操作問題。每個事務讀到的數(shù)據(jù)項都是一個歷史快照(snapshot)并依賴于實現(xiàn)的隔離級別。寫操作不覆蓋已有數(shù)據(jù)項,而是創(chuàng)建一個新的版本,直至所在操作提交時才變?yōu)榭梢???煺崭綦x使得事物看到它啟動時的數(shù)據(jù)狀態(tài)
數(shù)據(jù)庫事務的隔離級別
為了實現(xiàn)事務的隔離性,ISO 標準組織對事務鎖需要實現(xiàn)的隔離級別有四種定義,下面我們先對四種事務隔離的級別簡單闡述一下。
READ UNCOMMITTED 讀未提交
RU(READ UNCOMMITTED) 被稱為讀未提交,有些資料稱之為瀏覽穩(wěn)定(browse access)但是正確的翻譯應該是未提交讀。RU是最低標準的隔離,未提交讀的意思就是在事務并發(fā)的情況下,可以容許一個事務在沒有提交修改的的情況下被另外一個事務讀取到這個修改,這就就會產(chǎn)生臟讀的情況。下面這個表格是各個事務隔離級別對于臟讀、幻讀、可重復讀的抑制情況,事實上RU不但會產(chǎn)生臟讀的情況而且其他兩種讀的情況都會發(fā)生。
首先我們有必要澄清一下以上三種數(shù)據(jù)讀問題的概念,對于數(shù)據(jù)庫事務來說我們簡單的認識是一系列的數(shù)據(jù)庫操作在一個事務中,這個事務要不全部成功要不全部失敗,但是要知道數(shù)據(jù)庫在實際使用的過程中不是串行的,它是并發(fā)的,串行場景下我們事先事務就非常簡單了,就是一個一個操作嘛,大家排隊執(zhí)行。但是在并發(fā)事務的場景下就會出現(xiàn)對同一個數(shù)據(jù)的競爭問題,簡單的理解就是你也要讀寫這個數(shù)據(jù),我也要讀寫這個數(shù)據(jù),那么大家多個事務操作一個數(shù)據(jù)的時候怎么保證數(shù)據(jù)的一致和完整?這個時候就會出現(xiàn)數(shù)據(jù)的臟讀、幻讀、重復讀問題。
當一個事務允許讀取另外一個事務修改但未提交的數(shù)據(jù)時,就可能發(fā)生臟讀(dirty reads)
臟讀是指多個事務同時讀寫一個數(shù)據(jù),當事務1中修改和讀取數(shù)據(jù)A時,事務2對數(shù)據(jù)A做了修改,然后這個修改反映到了事務A中。
我們試想有這樣的場景,假如兩個事務都在操作金額表中的同一條記錄,事務A需要獲得到當前金額值然后給他做加3的操作(用于買黃瓜),原來這個金額的值是5,但是此時事務B將這條數(shù)據(jù)的金額修改成了8,然后這個修改被事務A拿到然后在8的基礎上加了3等于11。但是萬萬沒想到在A事務做完這個操作以后B事務回滾了(反悔了,香蕉的錢沒給)。這個時候A事務完成以后賬戶的金額莫名其妙的變成了11,但是事實上應該是8。這也就是臟讀的情況。
在一次事務中,當一行數(shù)據(jù)獲取兩遍得到不同的結果表示發(fā)生了不可重復讀(non-repeatable reads)
在理解不可重復讀之前先理解什么是可重復讀,可重復讀的意思就是在一個事務中對同一個數(shù)據(jù)的多次讀取其結果應該是相同的(在這個事務中沒有修改它的值)。那么反過來的意思就是在一個事務中對一個數(shù)據(jù)的多次讀取的值是不一樣的,什么情況下會出現(xiàn)不可重復讀呢?
還是上面的例子,假如事務A在做加3操作之前先讀取了原來的值也就是5,然后繼續(xù)其他操作,這個時候事務B對這條記錄進行了加3的操作然后提交了,當事務A再次讀這個值的時候發(fā)現(xiàn)當前值變成了8,這個時候前后兩次的值完全不一樣,這也就是不可重復讀。
不可重復讀是針對單個事務來說的,也就是在一個事務中是否可以對一條數(shù)據(jù)做重復的讀取,如果不能,那么也就意味著不滿足可重復讀的要求。
不可重復讀和臟讀非常類似,但是兩者是有區(qū)別的臟讀是指事務2沒有提交這個修改就被事務1獲取到了修改后的值,而不可重復讀是指提交了修改以后產(chǎn)生了不一致的情況。
幻讀
在事務執(zhí)行過程中,當兩個完全相同的查詢語句執(zhí)行得到不同的結果集。這種現(xiàn)象稱為幻讀(phantom read)
幻讀實際上是不可重讀的一種場景,比如在事務1中,第一次按照某個條件讀取到了3條數(shù)據(jù),但是此時事務2在這個表中添加了一條滿足此條件的數(shù)據(jù),在事務1第二次讀的時候發(fā)現(xiàn)多了一條數(shù)據(jù)(反過來就是少了一條數(shù)據(jù)),這時候?qū)τ谑聞?來說就有點莫名其妙了,貌似產(chǎn)生了幻覺(發(fā)多貨了),所以稱之為幻讀。
所以針對未提交讀這種隔離級別,這三種讀問題都有可能產(chǎn)生,所以它是級別最低的事務隔離。
READ COMMITTED 讀提交
RC(READ COMMITTED) 讀提交是指在提交以后可以讀,有些資料稱之為提交讀(國內(nèi)翻譯也是醉了)。提交讀主要針對的場景是UPDATE語句,就是針對更新只有提交了以后才能讀,試想一下在上面介紹臟讀的時候,如果事務2在修改完金額以后提交了這個值而不是回滾,那么久沒有臟讀的情況。
這也就是為什么提交讀只能解決臟讀的問題而不能解決其他兩種讀的問題。因為很顯然就算事務2提交了這次修改,那么對于事務1來說前后兩次的讀取都是不一致的(不可重復讀),當然幻讀的場景更是存在了,因為幻讀本來就是不可重復讀的特殊場景。
REPEATABLE READS 可重復讀
RR(REPEATABLE READS)可重復讀是僅次于SERIALIZABLE(串行化)的一種事務隔離級別,通??芍貜妥x是通過鎖實現(xiàn)的,它避免不了幻讀的產(chǎn)生。在InnoDB中默認采用RR這種事務隔離級別,但是和其他數(shù)據(jù)庫不同的是InnoDB在在RR的事務隔離級別下采用了NKL的鎖算法(Next-Key Lock),避免了幻讀的產(chǎn)生。這與其他數(shù)據(jù)庫不同,所以在InnoDB中RR的事務隔離級別達到了串行化的事務隔離標準。
NKL是指鎖定一個范圍和數(shù)據(jù)本身,而不是只單單鎖定數(shù)據(jù)本身,這樣能夠避免幻讀的產(chǎn)生,官方文檔
SERIALIZABLE 可串行化
是最高級別的事務隔離,按照定義是指所有事務都按照串行化進行執(zhí)行,也就是沒有并發(fā)事務的產(chǎn)生,這樣就避免了所有讀問題,但是這對于數(shù)據(jù)庫來說是不可能的,因為任何一個數(shù)據(jù)庫都不能忍受這種情況,所以大多數(shù)人認為采用這種事務隔離級別會對性能產(chǎn)生非常大的影響,但是有些論文通過實驗得出串行化并不會對性能產(chǎn)生太大的影響。
關于串行化是不是對性能產(chǎn)生影響,這取決于數(shù)據(jù)庫對這種事務隔離級別的實現(xiàn),不能完全說串行就一定慢,反正我是不知道是不是真的對性能影響很大。
MySQL數(shù)據(jù)庫事務隔離級別查詢和修改
查詢事務隔離級別
在MySQL中我們可以通過以下方式查詢數(shù)據(jù)庫采用的事務隔離級別
show variables like '%tx_isolation%'; # 查詢回話的事務隔離級別 SELECT @@session.tx_isolation; #查看全局的隔離級別 SELECT @@global.tx_isolation;
修改事務隔離級別
MySQL 提供了 SET TRANSACTION 語句,該語句可以改變單個會話或全局的事務隔離級別。語法格式如下:
SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}
其中,SESSION 和 GLOBAL 關鍵字用來指定修改的事務隔離級別的范圍
SESSION:表示修改的事務隔離級別將應用于當前 session(當前 cmd 窗口)內(nèi)的所有事務;
GLOBAL:表示修改的事務隔離級別將應用于所有 session(全局)中的所有事務,且當前已經(jīng)存在的 session 不受影響;
如果省略 SESSION 和 GLOBAL,表示修改的事務隔離級別將應用于當前 session 內(nèi)的下一個還未開始的事務。
任何用戶都能改變會話的事務隔離級別,但是只有擁有 SUPER 權限的用戶才能改變?nèi)值氖聞崭綦x級別
JDBC 修改當前連接的隔離級別
connection.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
MySQL 數(shù)據(jù)庫
版權聲明:本文內(nèi)容由網(wǎng)絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權內(nèi)容。
版權聲明:本文內(nèi)容由網(wǎng)絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權內(nèi)容。