MySQL 原理篇數據庫
MySQL 索引機制緩存
MySQL 體系結構及存儲引擎安全
MySQL 語句執行過程詳解併發
MySQL 執行計劃詳解app
MySQL InnoDB 緩衝池高併發
MySQL InnoDB 事務性能
MySQL InnoDB 鎖spa
MySQL InnoDB MVCC設計
MySQL InnoDB 實現高併發原理3d
MySQL InnoDB 快照讀在RR和RC下有何差別
MVCC
MVCC 的定義
MVCC(Multiversion concurrency control):多版本併發控制,併發訪問(讀或寫)數據庫時,對正在事務內處理的數據作多版本的管理。以達到用來避免寫操做的堵塞,從而引起讀操做的併發問題。
MVCC 邏輯流程
插入
MySQL 在每一行數據中都會默認添加一些隱藏列 DB_TRX_ID、DB_ROLL_PT。
上面圖中的執行步驟以下:
- 手動開啓事務,從 InnoDB 引擎中獲取一個全局事務ID(1)
- 而後往 teacher 表中插入兩條數據,同時設置數據行的版本號爲當前事務ID,刪除版本號爲 NULL
思考:若是事務是自動提交的(SET AUTOCOMMIT = NO),且未手動開啓事務,執行以下兩條 SQL,插入的數據會是什麼樣子的?
INSERT INTO teacher (NAME, age) VALUE ('seven', 18) ;
INSERT INTO teacher (NAME, age) VALUE ('qingshan', 19) ;
由於事務是自動提交的,因此兩條插入語句會分別獲取事務ID,因此這裏插入的數據行的版本號是1和2。
刪除
上面圖中的執行步驟以下:
- 手動開啓事務,從 InnoDB 引擎中獲取一個全局事務ID(22)
- 而後執行一條刪除語句,InnoDB 會找到這條記錄,把它的刪除版本號設置爲當前事務ID
修改
上面圖中的執行步驟以下:
- 手動開啓事務,從 InnoDB 引擎中獲取一個全局事務ID(33)
- 而後執行一條修改語句,InnoDB 會找到這條記錄,copy 一份原數據插入到表中,將新行數據的數據行的版本號的值設置爲當前事務ID,將原行數據的刪除版本號的值設置爲當前事務ID
查詢
上面圖中的執行步驟以下:
- 手動開啓事務,從 InnoDB 引擎中獲取一個全局事務ID(44)
- 根據數據查詢規則的描述
- 查找數據行版本早於當前事務版本的數據行,發現表中三行數據都知足條件
- 查找刪除版本號要麼爲 NULL,要麼大於當前事務版本號的記錄,發現只有最後一條數據知足條件(1, seven, 19)
案例分析
數據準備:
CREATE TABLE `teacher` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(32) NOT NULL,
`age` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;
INSERT INTO teacher(id,NAME,age) VALUES (1,'seven',18);
INSERT INTO teacher(id,NAME,age) VALUES (2,'qingshan',20);
案例一
-- 事務A執行
BEGIN; -- 1
SELECT * FROM teacher; -- 2
COMMIT;
--事務B執行
BEGIN; -- 3
UPDATE teacher SET age =28 WHERE id=1; -- 4
COMMIT;
案例一的執行步驟是:1,2,3,4,2,執行效果以下圖所示:
雖然在執行 3,4 步驟的時候更新 id=1 的數據,可是根據 MVCC 的查詢邏輯流程,再次執行2,獲取到的數據依然和第一次同樣。
案例二
-- 事務A執行
BEGIN; -- 1
SELECT * FROM teacher; -- 2
COMMIT;
--事務B執行
BEGIN; -- 3
UPDATE teacher SET age =28 WHERE id=1; -- 4
COMMIT;
案例二的執行步驟是:3,4,1,2,執行效果以下圖所示:
根據 MVCC 的查詢邏輯流程,執行1,2,獲取到的數據是事務B未提交的數據,這個是有問題的。
分析了案例一和案例二,發現 MVCC 不能解決案例二的問題,InnoDB 會使用 Undo log 解決案例二的問題。
Undo Log
Undo Log 的定義
Undo:意爲取消,以撤銷操做爲目的,返回指定某個狀態的操做。
Undo Log:數據庫事務提交以前,會將事務修改數據的鏡像(即修改前的舊版本)存放到 undo 日誌裏,當事務回滾時,或者數據庫奔潰時,能夠利用 undo 日誌,即舊版本數據,撤銷未提交事務對數據庫產生的影響。。
- 對於 insert 操做,undo 日誌記錄新數據的 PK(ROW_ID),回滾時直接刪除;
- 對於 delete/update 操做,undo 日誌記錄舊數據 row,回滾時直接恢復;
- 他們分別存放在不一樣的buffer裏。
Undo Log 是爲了實現事務的原子性而出現的產物。 Undo Log 實現事務原子性:事務處理過程當中,若是出現了錯誤或者用戶執行了 ROLLBACK 語句,MySQL 能夠利用 Undo Log 中的備份將數據恢復到事務開始以前的狀態。 |
InnoDB 發現能夠基於 Undo Log 來實現多版本併發控制。
Undo Log 在 MySQL InnoDB 存儲引擎中用來實現多版本併發控制。 Undo Log 實現多版本併發控制:事務未提交以前,Undo Log 保存了未提交以前的版本數據,Undo Log 中的數據可做爲數據舊版本快照供其餘併發事務進行快照讀。 |
分析下圖中 SQL 的執行過程。
- 事務A手動開啓事務,執行更新操做,首先會把更新命中的數據拷貝到 Undo Buffer 中
- 事務B手動開啓事務,執行查詢操做,會讀取 Undo Log 中數據返回,進行快照度
當前讀和快照讀
快照讀
SQL 讀取的數據是快照版本,也就是歷史版本,普通的 SELECT 就是快照讀。
InnoDB 快照讀,數據的讀取將由 cache(本來數據)+ Undo Log(事務修改過的數據)兩部分組成。
當前讀
SQL 讀取的數據是最新版本,經過鎖機制來保證讀取的數據沒法經過其餘事務進行修改。
UPDATE 、DELETE 、INSERT 、SELECT … LOCK IN SHARE MODE 、SELECT … FOR UPDATE 都是當前讀,這些操做在《MySQL InnoDB 鎖》這篇文章中有過演示,事務A執行這些 SQL,會阻塞事務B的 SQL 執行。
在 InnoDB 引擎裏面,快照讀經過 MVCC 解決幻讀的問題,當前讀經過 Next-Key Locks 解決幻讀的問題。
Redo Log
Redo Log 的定義
Redo:顧名思義就是重作。以恢復操做爲目的,重現操做。
Redo Log:指事務中操做的任何數據,將最新的數據備份到一個地方(Redo Log)。
Redo Log 的持久化:不是隨着事務的提交才寫入的,而是在事務的執行過程當中,便開始寫入 Redo Log 中,具體的落盤策略能夠進行配置。
Redo Log 是爲了實現事務的持久性而出現的產物。
Redo Log 實現事務持久性:防止在發生故障的時間點,尚有髒頁未寫入表的 IBD 文件中,在重啓 MySQL 服務的時候,根據 Redo Log 進行重作,從而達到事務的未入磁盤數據進行持久化這一特性。
根據下圖分析 Redo Log 的執行流程
InnoDB 不是每一次提交事務都把數據從緩存區持久化到硬盤的,由於每次提交事務都把數據持久化到硬盤,效率很低,每一次持久化都須要執行 IO 操做。
InnoDB 會把每次數據變化會先進入 Redo Buffer 中,事務提交了,會根據策略把新的數據寫入 Redo Log 中,InnoDB 就會認爲此次事務提交成功了,數據並不必定立刻就進入表的 IBD 文件中。
疑問:持久化到 Redo Log 中和持久化到表的 IBD 文件同樣都是 IO 操做,爲何要設計 Redo Log 呢?
實際上是由於持久化到 Redo Log 中是順序 IO 的操做,而持久化到表的 IBD 文件中是一個隨機 IO 的操做,好比咱們須要更新 id=1 和 id=8 的數據,若是是 Redo Log,就只須要把更新的數據順序存入 Redo Log 中;但若是是表的 IBD 文件,就須要先找到 id=1 和 id=8 的兩個不連續的磁盤文件地址,再作持久化操做,影響數據庫服務的併發性能。
Redo Log 的持久化配置
指定 Redo Log 記錄在 {datadir}/ib_logfile1 和 ib_logfile2 兩個文件中,能夠經過 innodb_log_group_home_dir配置指定目錄存儲。
一旦事務成功提交且數據持久化到表的 IBD 文件中以後,此時 Redo Log 中的對應事務數據記錄就失去了意義,所 以 Redo Log 的寫入是日誌文件循環寫入的過程,也就是覆蓋寫的過程。
- 指定 Redo Log 日誌文件組中的數量 innodb_log_files_in_group 默認爲2
- 指定 Redo Log 每個日誌文件最大存儲量 innodb_log_file_size 默認48M
- 指定 Redo Log 在 cache/buffer 中的 buffer 池大小 innodb_log_buffer_size 默認16M
Redo Buffer 持久化到 Redo Log 的策略,經過設置 Innodb_flush_log_at_trx_commit 的值:
- 取值0:每秒提交 Redo buffer -> Redo Log OS cache -> flush cache to disk,可能丟失一秒內的事務數據。
- 取值1(默認值):每次事務提交執行 Redo Buffer -> Redo Log OS cache -> flush cache to disk,最安全,性能最差的方式
- 取值2:每次事務提交執行 Redo Buffer -> Redo log OS cache 再每一秒執行 -> flush cache to disk 操做
通常建議選擇取值2,由於 MySQL 掛了最多損失一次事務提交的數據,整個服務期掛了纔會損失一秒的事務提交數據。