感謝你的三連,關(guān)注不迷路,我是Bill,一個5年京漂,有問題可以私信我,我會盡力幫助解決你的問題。
什么是事務(wù)?
數(shù)據(jù)庫事務(wù)( transaction)是訪問并可能操作各種數(shù)據(jù)項的一個數(shù)據(jù)庫操作序列,這些操作要么全部執(zhí)行,要么全部不執(zhí)行,是一個不可分割的工作單位。事務(wù)由開始和結(jié)束之間執(zhí)行的全部數(shù)據(jù)庫操作組成。
這是百科的定義,也是我們在面試的時候最?;卮鸬年P(guān)鍵字。
可以這么說:事務(wù)是數(shù)據(jù)庫執(zhí)行過程的一個邏輯單位,由一個有限的數(shù)據(jù)庫操作序列組成。
舉例來說,當我們購物下訂單時,有這么兩個操作(當然不止這倆):付款,減庫存。這兩個操作序列就是一個事務(wù),他不能拆分執(zhí)行,必須同時成功和失敗。
當我們面試時最常遇到的問題是什么?
或者不那么世俗的說——畢竟我們學習并不全是為了面試嘛,啊呸!不面試誰看那么多,腦子內(nèi)存本來就不大。

編輯搜圖
請點擊輸入圖片描述
事務(wù)的4大特性
A (Atomicity) 原子性:事務(wù)的操作序列不可再拆分:這也是都成功都失敗的意思。
C (Consistent) 一致性:事務(wù)的執(zhí)行使數(shù)據(jù)從一個狀態(tài)轉(zhuǎn)換為另一個狀態(tài),但是對于整個數(shù)據(jù)的完整性保持穩(wěn)定。
I(Isolation)隔離性:隔離性是當多個用戶并發(fā)訪問數(shù)據(jù)庫時,比如操作同一張表時,數(shù)據(jù)庫為每一個用戶開啟的事務(wù),不能被其他事務(wù)的操作所干擾,多個并發(fā)事務(wù)之間要相互隔離。
D(Durable)持久性:久性是指一個事務(wù)一旦被提交,它對數(shù)據(jù)庫中數(shù)據(jù)的改變就是永久性的,接下來即使數(shù)據(jù)庫發(fā)生故障也不應(yīng)該對其有任何影響。
關(guān)于ACID搶眼回答:
數(shù)據(jù)庫的每一個操作其實是一條日志。
原子性,在 InnoDB 里面是通過 undo log 來實現(xiàn)的,它記錄了數(shù)據(jù)修改之前的值(邏輯日志),一旦發(fā)生異常,就可以用 undo log 來實現(xiàn)回滾操作。
持久性怎么實現(xiàn)呢?數(shù)據(jù)庫崩潰恢復(fù)(crash-safe)是通過什么實現(xiàn)的?
是通過 redo log 和 double write 雙寫緩沖來實現(xiàn)的,我們操作數(shù)據(jù)的時候,會先寫到內(nèi)存的buffer-pool中,同時記錄 redo log,如果在刷盤之前出現(xiàn)異常,在重啟后就可以讀取 redo log 的內(nèi)容,寫入到磁盤,保證數(shù)據(jù)的持久性。
當然,恢復(fù)成功的前提是數(shù)據(jù)頁本身沒有被破壞,是完整的,如果數(shù)據(jù)頁本身損壞了,這個可以通過雙寫緩沖 (double write)保證。
說了這么多事務(wù),你甚至可能在實際編程中并沒有手動操作過事務(wù),那么如何手動開啟和關(guān)閉事務(wù)呢?

編輯搜圖
請點擊輸入圖片描述
Mysql數(shù)據(jù)庫默認是開啟自動提交事務(wù)的,也就是說當你執(zhí)行update/delete/insert操作時數(shù)據(jù)庫引擎會自動開啟一個事務(wù),并且在操作完成后自動提交事務(wù)。
事務(wù)并發(fā)產(chǎn)生的問題
臟讀,讀到了其他事務(wù)還沒提交的數(shù)據(jù)
不可重復(fù)讀,讀到了其他事務(wù)提交之后的數(shù)據(jù),數(shù)據(jù)變化了(update/delete)
幻讀,讀到了其他事務(wù)提交之后的數(shù)據(jù),只有數(shù)據(jù)增加了行才叫幻讀(insert)
你可能會說,事務(wù)不是有隔離性嗎,為什么還有這么多問題?好了繼續(xù)往下看
事務(wù)隔離級別
- Read Uncommited 未提交讀,顧名思義,可以讀到其他事務(wù)沒有提交的數(shù)據(jù),會產(chǎn)生臟讀
事務(wù)操作時并不隔離其他的事務(wù),比如以下操作:

編輯搜圖
請點擊輸入圖片描述
而如果此時事務(wù)1 回滾了事務(wù),事務(wù)2就讀到了臟數(shù)據(jù);
這種隔離機制在數(shù)據(jù)庫實際使用中,顯然不合適,但有助于理解其他事務(wù)。
- Read Commited 已提交讀,只能讀取其他事務(wù)已經(jīng)提交的數(shù)據(jù),不能讀取到其他事務(wù)未提交的數(shù)據(jù),它解決了臟讀的問題, 但是會出現(xiàn)不可重復(fù)讀的問題。
事務(wù)只能讀取其他事務(wù)提交之后的數(shù)據(jù),我們來模擬以下這個過程會出現(xiàn)的問題:

編輯搜圖
請點擊輸入圖片描述
可以看到,這種隔離級別會導(dǎo)致數(shù)據(jù)在一個事務(wù)兩次讀取的數(shù)據(jù)不一致,也就是不可重復(fù)讀;
- Repeatable Read 可重復(fù)讀,它解決了不可重復(fù)讀的問題, 也就是在同一個事務(wù)里面多次讀取同樣的數(shù)據(jù)結(jié)果是一樣的,但是在這個級別下,沒有解決幻讀的問題。
這也是最常用的事務(wù)隔離機制,他可以保證在一個事務(wù)里讀取的數(shù)據(jù)是一樣的,也就是數(shù)據(jù)的可重復(fù)讀。你可能會問,數(shù)據(jù)庫是如何實現(xiàn)可重復(fù)讀的?這個問題會在之后解答,先來看看什么是幻讀。
一個事務(wù)前后兩次讀取數(shù)據(jù)數(shù)據(jù)不一致,是由于其他事務(wù)插入數(shù)據(jù)造成的,這種情況我們把它叫做幻讀。
比如:

編輯搜圖
請點擊輸入圖片描述
第二次查詢我們發(fā)現(xiàn)多了一條數(shù)據(jù),這就叫幻讀。需要記住的是只有其他事務(wù)插入導(dǎo)致的數(shù)據(jù)不一致才叫幻讀。
- Serializable(串行化),最后一個是串行的隔離級別,也是最嚴格的隔離級別,它要求事務(wù)必須排隊執(zhí)行,解決了幻讀。但這大大影響了效率。也比較少用。
所以你會說,既然我們比較常用的是RR(可重復(fù)讀)的隔離級別會有幻讀的情況,那為什么還經(jīng)常使用它?其實在MySQL的InnoDB引擎使用RR的隔離級別但配合鎖的機制已經(jīng)避免了幻讀情況。
我們先來看一下MySQL是如何實現(xiàn)數(shù)據(jù)的可重復(fù)的。
有兩種方案。
LBCC (Lock Based Concurrency Control),意思是讀取數(shù)據(jù)的時候鎖定它,不允許其他事務(wù)操作,數(shù)據(jù)自然也不會變化。這種方案我們叫做基于鎖的并發(fā)控制簡稱LBCC。一個事務(wù)讀取的時候不允許其他時候修改,那就意味著不支持并發(fā)的讀寫操作,而我們的大多數(shù)應(yīng)用都是讀多寫少的,這樣會極大地影響操作數(shù)據(jù)的效率。
MVCC (Multi Version Concurrency Control), 意思是多版本并發(fā)控制,另一種解決方案是如果要讓一個事務(wù)前后兩次讀取的數(shù)據(jù)保持一致, 那么我們可以在修改數(shù)據(jù)的時候給它建立一個備份或者叫快照,后面再來的事務(wù)讀取這個快照就行了。
MVCC的核心思想是:可以查到在當前事務(wù)開始之前已經(jīng)存在的數(shù)據(jù),即使它在后面被其他事務(wù)修改或者刪除了。而當前事務(wù)之后新增的數(shù)據(jù),當前事務(wù)是查不到的。
那么問題來了,如何保證當前事務(wù)數(shù)據(jù)的一致性呢?也就是說怎么保證數(shù)據(jù)被其他事務(wù)修改和刪除或者新增了數(shù)據(jù),而當前事務(wù)并不受影響呢?
讀取數(shù)據(jù)事務(wù)開始的時候,MySQL為事務(wù)創(chuàng)建了快照,也就是在事務(wù)內(nèi)查詢的數(shù)據(jù)都是快照版本,這樣就可以保證數(shù)據(jù)的一致性。
那么快照又是如何實現(xiàn)的呢?
InnoDB 為每行記錄都實現(xiàn)了兩個隱藏字段:
DB_TRX_ID,6 字節(jié):插入或更新行的最后一個事務(wù)的事務(wù)ID,事務(wù)編號是自動遞增的(我們把它理解為創(chuàng)建版本號,在數(shù)據(jù)新增或者修改為新數(shù)據(jù)的時候,記錄當前事務(wù)ID)
DB_ROLL_PTR,7 字節(jié):回滾指針(我們把它理解為刪除版本號,數(shù)據(jù)被刪除或記錄為舊數(shù)據(jù)的時候,記錄當前事務(wù) ID)。
我們把這兩個事務(wù)ID理解為版本號。
從插入數(shù)據(jù)開始,我們來看一下MySQL如何用這兩個版本號來隔離事務(wù)。

編輯搜圖
請點擊輸入圖片描述
此時又有一個事務(wù)進來,增加了一條數(shù)據(jù)并提交結(jié)束。

編輯搜圖
請點擊輸入圖片描述
MVCC 的查找規(guī)則1:只能查找創(chuàng)建時間小于等于當前事務(wù) ID 的數(shù)據(jù)

編輯搜圖
請點擊輸入圖片描述
再次回到事務(wù)2查詢

編輯搜圖
請點擊輸入圖片描述
MVCC 的查找規(guī)則2: 能查找刪除時間大于當前事務(wù)id的數(shù)據(jù),也就是在事務(wù)之后刪除的數(shù)據(jù)在當前事務(wù)依然能查得到。
事務(wù)5,嘗試修改數(shù)據(jù)

編輯搜圖
請點擊輸入圖片描述
此時回到事務(wù)2再次查詢數(shù)據(jù)

編輯搜圖
請點擊輸入圖片描述
按照查找規(guī)則:只能查找創(chuàng)建時間小于等于當前事務(wù) ID 的數(shù)據(jù),和刪除時間大于當前事 務(wù) ID 的行(或未刪除)。
解釋:id為1的數(shù)據(jù)刪除版本大于當前事務(wù)2,創(chuàng)建版本小于事務(wù)2,依然可以查到,
id為2的數(shù)據(jù)創(chuàng)建版本為1的數(shù)據(jù),小于事務(wù)2,刪除版本大于事務(wù)2,可以查到
id為3的數(shù)據(jù)創(chuàng)建版本為3大于事務(wù)2,查不到;
id為2的數(shù)據(jù)創(chuàng)建版本為5的數(shù)據(jù),大于事務(wù)2,依然查不到;
所以,在事務(wù)2內(nèi),無論外部發(fā)生了什么翻天覆地的變化,事務(wù)2自始自終查到的都是自己的快照數(shù)據(jù),保證了數(shù)據(jù)的一致性,一定要理解MVCC的核心思想。
之后會更新結(jié)合鎖機制,InnoDB是如何在RR級別解決幻讀的。
祝你好運,關(guān)注我嘛~