今天開始的主題有關於MYSQL的crash-safe能力(二階段提交),如何保證服務在任何時間發生崩潰時,重啟後之前的提交紀錄不會發生數據丟失狀況。
事務提交流程
在binlog內容中我們可以看到事件的上下文都會有紀錄BEGIN和COMMIT 代表這個事件的開始與結束,因為預設autocommit為開啟,所以當我們輸入一條SQL指令,MYSQL就會自動幫我們提交執行內容(等於在binog裡看到的一個事件)。
透過以下簡單例子了解開啟與關閉自動提交的差異~ 開啟2個連線而右邊的連線只做查詢資料動作。
ps. DDL語句是無法進行rollback 的喔!!
儲存引擎
就像是資料庫的核心,以MYSQL來說常見的有InnoDB&MyISAM。MYSQL 5.5版開始所使用的(預設引擎)是InnoDB,不同引擎有各自優缺點與應用需求,這邊就不多描述囉~
#看支持的引擎有哪些
mysql> show engines;
9 rows in set (0.00 sec)
mysql> show variables like 'default_storage_engine';
+------------------------+--------+
| Variable_name | Value |
+------------------------+--------+
| default_storage_engine | InnoDB |
+------------------------+--------+
為什麽會看到敘述時有時候用(事務)有時候用(事件),透過show engines可以發現除了InooDB其他引擎都是不支援事務的,像常見的MyISAM ~ 所以才會以事件敘述避免混淆。不過現在預設就是使用InnoDB所以看成同樣的就行啦~
認識一點innoDB架構
由於二階段流程上會牽扯到一些innoDB架構內部機制流程,所以這邊先帶一點innoDB架構相關內容在進入正題比較好了解意思。
(引用mysql官方5.7的InnoDB架構圖) 先看一下架構中(左)內存區&(右)磁盤區分別包含什麼~
觀念: 資料庫中的數據最終都是要存放到磁盤上的,不過在磁盤的讀取速度會比內存慢很多,所以不可能每次讀寫資料都往磁盤執行,勢必造成效能上的影響。
數據頁(Page)- InnoDB儲存數據的基本結構,頁會儲存資料庫相關數據內容,作為磁盤與內存交互的最基本單位。
髒頁- 指的是在緩衝池中已經被修改的頁,還沒被刷新到磁盤上。
(ex.數據頁A 在buffer pool中的內容比在disk中的還要新= 髒頁)
那身為一個儲存資料系統,資料會頻繁的做存取是很正常的。透過緩衝池在資料讀取上緩存訪問時的表和索引數據,去達到加快讀取請求的處理速度提升效能。另外執行資料的更新異動時也是先在buffer pool中完成的喔!
觀念: 如果內存中不存在要讀取or更新的數據,會先從disk載入數據所在數據頁至buffer pool中在做更新,而不是直接透過disk去存取資料。ex.讀取一條數據
查詢語句 -> (判斷有無命中)
判斷數據頁是否存在於內存中 ---> if N (從磁盤中將該紀錄對應的數據頁加載至內存)
---> if Y 返回數據
MYSQL崩潰復原Q&A
Q1: 如果內存中的數據已更新(磁盤上的數據尚未更新),那當MySQL服務在任何時間發生異常關閉的狀況下,如何去保證重啟後還尚未落盤的內存資料不會丟失?
A1: mysql如何實現crash-safe保證事務完整性,靠的就是innoDB架構中的redo log和undo log日誌紀錄,在提交階段中如果發生意外崩潰,對於已提交的事務資料就不會造成丟失(redo log),而對於提交尚未完整的事務則會進行rollback(undo log)。
Q2: 為什麽binlog無法做到crash-safe能力,而redo log可以做到?
來看一下兩者之間差異:
-- | binlog | redo log
------------- | -------------
層級 | Server | InnoDB
儲存內容 | 邏輯日誌 | 物理日誌
寫入方式 | 追加寫(參數設定切換) | 循環寫(固定大小)
內容 | SQL語句邏輯 |事務執行過程中對數據頁的修改
作用 | mysql主從,備份,還原 | mysql崩潰恢復
有關於redo log需了解以下內容:
參考上圖InnoDB架構內存區域,Log Buffer包含了保存要寫入磁盤InnoDB日誌文件(redo log)的數據。
redo log
可分為在內存中的日誌緩衝(redo log buffer) 和在磁盤上的日誌文件(redo log file)。
ex.在mysql數據目錄下會看到 ib_logfile0,ib_logfileN 的文件。可以發現redo log不只有一個儲存文件,有ib_logfile0~ib_logfileN 個。
透過以下參數配置redo log文件數量&大小 ex.預設為2個redo log文件檔
mysql> show variables like '%innodb_log_file%';
+---------------------------+----------+
| Variable_name | Value |
+---------------------------+----------+
| innodb_log_file_size | 50331648 |
| innodb_log_files_in_group | 2 |
+---------------------------+----------+
2 rows in set (0.01 sec)
ps.在innodb_log_file_size 的設定上對inooDB性能面來說是有相對影響的!像設置太大的值可能會造成崩潰恢復時的時間拉長。
這邊開始要想一下喔~
注意: 當我們執行一條DML語法時會先將記錄寫入內存中的redo log buffer(紀錄修改後的值),事務提交時再將其寫至磁盤上redo log file,最後定期將內存中更改的數據(髒頁)刷新至磁盤上。
以下圖幫助你認識redo log文件資料的紀錄(write pos)與清除(checkpoint)作用
WAL機制(Write-Ahead Logging)- 在redo log採用。
是指寫操作時不會立刻更新至磁盤上,而是先紀錄到日誌,等到某個時機點在更新至磁盤上。而由於寫入磁碟檔案是隨機寫,寫入日誌為順序寫。相比之下順序寫的方式造成的開銷與效能相對小,所以使用日誌先行的機制。
一般會看到日誌先行的解釋為先寫入日誌在寫入磁盤,不過不知道實際上流程是指什麼~
解釋: 先將內存中對應的日誌頁持久化(redo log buffer-寫入->redo log file),才持久化內存的更新數據頁至磁盤上。
A2: 看完以上比較可以看到由於redolog是固定大小的,不斷的循環寫。所以當日誌滿了的狀況下,就會需要對舊的紀錄做刪除,而刪除的前提是這些數據頁內容已經從內存刷入磁盤中!以儲存內容來說binlog沒有能判斷哪些數據是否已刷盤的標示。所以當mysql掛了重啟時會使用redo log紀錄進行復原,來保證數據持久完整性。
明天來講關於redo log和binlog日誌寫入策略影響~