目錄
快,開篇大夥先思考乙個問題,mysql 是怎麼保證資料不丟失的呢?
其實要保證資料不丟失,說白了要具有下面兩種能力:
(1)能恢復到任何時間點的狀態;
(2)能保證 mysql 在任何時間段突然宕機重啟,已提交的資料不會丟失,未提交完整的資料也會自動回滾;
這不就引出來今天要聊的主題了麼,實現第一點需要用 bin log,實現第二點需要用 redo log 和 undo log。
了解三大log之前,我們先看一下mysql資料更新的流程:
上面這張圖包含了 redo log、bin log、undo log 三種日誌之間的大致關係,下面進入正題。
我們知道 mysql 資料存在磁碟中,每次讀寫資料需做磁碟 io,併發場景下效能差。為此 mysql 引入快取buffer pool做優化。其包含磁碟中部分資料頁(page)的對映,來緩解資料庫的磁碟壓力。
當從資料庫讀資料時,首先從快取中讀,快取中沒有,則從磁碟讀後放入快取;當向資料庫寫資料時,先向快取中寫,此時快取中的資料頁資料會變更,該資料頁叫髒頁,buffer pool 中修改完資料後會按照設定的策略再定期刷到磁碟中去,這個過程叫刷髒頁。
那麼問題來了,如果 buffer pool 中修改的資料還沒有及時的刷到磁碟,mysql 宕機重啟,就會導致資料丟失,無法保證事務的永續性,怎麼辦?
redo log 解決了這個問題。就是說資料庫在修改資料時,會把更新記錄先寫到 redo log 中,再去修改 buffer pool 中的資料,當提交事務時,呼叫 fsync 把 redo log 刷入磁碟。至於快取中更新的資料檔案何時刷入磁碟,則由後台執行緒非同步處理。
注意:此時 redo log 的事務狀態是 prepare,還未真正提交成功,要等 bin log 日誌寫入磁碟完成後才會變為 commit,事務才算真正提交成功。
redo log 的寫入方式?
redo log 採用大小固定,迴圈寫入的方式,當寫滿後,會重新從頭開始迴圈寫,類似乙個環狀。這樣設計原因是 redo log 記錄的是資料頁上的修改,如果 buffer pool 中資料頁已經刷到磁碟,這些記錄就失效了,新日誌會將這些失效的記錄覆蓋擦除。
注意:redo log 滿了,在擦除之前,要確保這些要被擦除記錄都已經刷到磁碟中了。在擦除舊記錄釋放新空間期間,不能再接收新的更新請求,此時 mysql 效能會下降。因此高併發情況下,合理調整 redo log 大小很重要。
crash-safe 能力是什麼?
innodb 引擎有 crash-safe 能力,即事務提交過程中任何階段,mysql 宕機重啟後都能保證事務的完整性,已提交的資料不會丟失。這種能力是通過redo log保證的,mysql 宕機重啟,系統將自動檢查 redo log,將修改還未寫入磁碟的資料從 redo log 恢復到 mysql 中。
undo log 記錄的是資料修改之前的狀態,屬於邏輯日誌,起到回滾的作用,是保證事務原子性的關鍵。
舉個栗子:假如更新 id=1 記錄的 name 字段,name 原始資料為小王,現改 name 為小張,事務執行 update x set name = 小張 where id =1 語句時,先在 undo log 中記錄一條相反邏輯的 update x set name = 小王 where id =1 記錄,這樣當某些原因導致事務失敗,就可借助 undo log 將資料回滾到事務執行前的狀態。
那麼問題來了:同乙個事務的一條記錄被多次修改,難道每次都要把資料修改前的狀態寫 undo log 嗎?
不會,因為 undo log 只記錄事務開始前資料的原始版本,當再次對這行資料修改時,產生的修改記錄會寫到 redo log。undo log 負責回滾,redo log負責前滾。
啥是回滾程式設計客棧和前滾?
(1)回滾
未提交的事務,即事務未執行 commit。但事務內修改的髒頁中,有一部分已刷盤。此時資料庫宕機重啟,需要回滾來將先前那部分已經刷盤的髒塊從磁碟上撤銷。
(2)前滾
未完全提交的事務,即事務已經執行 commit,但該事務內修改的髒頁中只有一部分資料被刷盤,另一部分還在 buffer pool,此時資料庫宕機重啟,就要用前滾來將未來得及刷盤的資料從 redo log 中恢復出來並刷盤。
bin log 記錄了使用者對資料庫所有 sql 操作(不包含查詢語句,因為這類操作對資料本身沒有修改)。之所以可以稱為歸檔日誌,是因為它不會像 redo log 那樣迴圈擦除之前的記錄,而是會一直記錄日誌。乙個 bin log 檔案預設最大容量1g(可通過 max_binlog_size 引數修改),單個日誌超過最大值則會新建立乙個檔案繼續寫。
注意:日誌可能是基於事務來記錄的,而事務不應該跨檔案記錄,如果 binlog 日誌檔案達到了最大值但剛好事務還沒有提交,此時則不會建立新檔案記錄,而是繼續增大日誌。因此 max_binlog_size 的值和實際的 binlog 檔案大小不一定相等。
經過上述介紹,binlog 主要用就是主從同步和資料庫基於時間點的還原。
那麼問題來了,可以沒有 binlog 嗎(有了 redo log 為啥還需要 bin log)?
需要分場景來看:
主從模式下,binlog 是必須的,因為從庫的資料同步需要依賴 binlog;
單機模式下,不考慮資料庫基於時間點的還原,binlog 就不是必須的,因為有 redo log 就可以保證 crash-safe 能力了;
redo log 的記錄修改落盤後,日誌會被覆蓋掉,無法用於資料恢復等操作,redo log 是 innodb 引擎層實現的,並不是所有引擎都有;
redo log 與 bin log 的區別?
什麼是 redo log 兩階段提交,為什麼要這麼做?
更新記憶體後引擎層寫 redo log 將狀態改成 prepare 為提交第一階段,server 層寫 bin log,將狀態改成 commit 為提交第二階段。 兩階段提交目的是確保 binl og 和 redo log 資料一致性。
如果不是兩階段提交可能會出現什麼情況?
1)假設先寫 redo log 再寫 bin log,即 redo log 沒有 prepare 階段,寫完直接置為commit,然後再寫 bin log。如果寫完 redo log 後還沒寫完 bin log 資料庫宕機了,重啟後系統自動用 redo log 恢復,此時會造成磁碟上資料頁資料比 bin log 上的記錄資料多,資料不一致。
2)假設先寫 bin log 再寫 redo log,如果寫完 bin log 沒寫完 redo log 資料庫宕機了,那麼 bin log 上的記錄就會比磁碟上資料頁的記錄多一些,下次用 bin 程式設計客棧log 恢復資料,恢復後的資料和原來的資料不一致。
描述一下 redo log 容災恢復過程?
如果 redo log 是完整(commit 狀態)的,直接用 redo log 恢復;
如果 redo log 是預提交 prepare 但不是 commit 狀態,此時要去判斷 binlog 是否完整,如果完整(commit)那就提交 redo log,再用 redo log 恢復,不完整就回滾事務。
資料庫之三大正規化
在前面我們已經介紹了 屬性值的概念,我們說,它是 不可分的 而第一正規化要求屬性也不可分。那麼它和屬性值不可分有什麼區別呢?給乙個例子 name address age小明 山東省濟寧市 13小紅 遼寧省大連市 12表中的address屬性實際上的可分的 這種情況不滿足第一正規化。不滿足第一正規化的...
資料庫設計之三大正規化
為了建立冗餘較小 結構合理的資料庫,設計資料庫時必須遵循一定的規則。在關係型資料庫中這種規則就稱為正規化。正規化是符合某一種設計要求的總結。要想設計乙個結構合理的關係型資料庫,必須滿足一定的正規化。在實際開發中最為常見的設計正規化有三個 1 第一正規化 確保每列保持原子性 第一正規化是最基本的正規化...
資料庫設計之三大正規化
背景 表設計後,很可能結構不合理,出現資料重複儲存,簡稱資料的冗餘,這對資料的增刪改查帶來很多後患,所以我們需要審核是否合理,如何審核呢?需要一些有關資料庫設計的理論指導規則,這些規則業界簡稱資料庫的正規化。兩個重要概念 1 部分函式依賴 1 存在資料冗餘 2 新增資料會有異常 3 更新資料會有異常...