圖解 MySQL 日志體系:讓你明明白白記住各種 Log
面試官:MySQL的日志系統(tǒng)了解嗎?
我:MySQL有binlog、redo log...
面試官:那它們分別解決什么問題?為什么需要這么多種日志?
我:這個(gè)...(尷尬)
相信這樣的對(duì)話很多同學(xué)都經(jīng)歷過。大家都知道MySQL有各種日志,但要說清楚它們的作用和關(guān)系,往往就不那么容易了。
作為一名有著7年MySQL開發(fā)經(jīng)驗(yàn)的老兵,今天我用最通俗的語言,幫你徹底理解MySQL日志體系。

一、為什么MySQL需要日志系統(tǒng)?
讓我們從一個(gè)最基礎(chǔ)的數(shù)據(jù)庫操作說起:
// 用戶消費(fèi)100元
UPDATE account SET balance = balance - 100 WHERE id = 1;這條簡單的SQL語句,實(shí)際上給數(shù)據(jù)庫帶來了三大挑戰(zhàn):
- 可靠性問題:如果數(shù)據(jù)庫突然宕機(jī),這筆交易記錄會(huì)不會(huì)丟失?
- 一致性問題:如果用戶要求退款,如何安全地回滾這筆交易?
- 同步問題:如何確保其他數(shù)據(jù)庫節(jié)點(diǎn)也正確記錄了這筆交易?
為解決這些問題,MySQL設(shè)計(jì)了三種核心日志:
數(shù)據(jù)庫操作 ──────────────────┐
↓
┌─── Redo Log(臨時(shí)記事本)
│ 記錄:"賬戶1減少100元"
│ 作用:確保交易記錄不丟失
│
├─── Undo Log(原始憑證)
│ 記錄:"賬戶1原有500元"
│ 作用:隨時(shí)可以撤銷交易
│
└─── Binlog(總賬本)
記錄:"完整交易記錄"
作用:用于數(shù)據(jù)同步和備份
二、重做日志(Redo Log):數(shù)據(jù)庫的"草稿紙"
1. 場景:會(huì)計(jì)小徐的煩惱
超市收銀員小徐要記錄1000筆交易。
傳統(tǒng)方式(沒有Redo Log):
-- 每筆交易都要立即寫入硬盤
UPDATE accounts SET balance = balance + 100;
UPDATE products SET stock = stock - 1;
結(jié)果:
┌─────────────────────┐
│ ? 頻繁隨機(jī)寫入硬盤 │
│ ? I/O效率極低 │
│ ? 系統(tǒng)性能下降 │
└─────────────────────┘Redo Log的解決方案:
1. 先寫入速記本(Redo Log)
┌────────────────────┐
│ 交易1: +100元,-1件 │
│ 交易2: +200元,-2件 │ ? 順序?qū)懭?,速度? │ ... │
└────────────────────┘
2. 數(shù)據(jù)先放內(nèi)存
┌────────────────┐
│ 內(nèi)存中快速匯總 │ ? 響應(yīng)迅速
└────────────────┘
3. 定期整理同步
┌────────────────┐
│ 批量寫入硬盤 │ ? 提高效率
└────────────────┘2. Redo Log工作原理詳解
以顧客購買2箱牛奶為例:
// 顧客買了2箱牛奶(每箱100元)
UPDATE accounts SET balance = balance + 200;
UPDATE stock SET quantity = quantity - 2;步驟1:速記本記錄(Redo Log)
在Redo Log中記錄交易信息:
交易編號(hào) | 時(shí)間 | 操作類型 | 表名 | 修改內(nèi)容 | 狀態(tài) |
001 | 09:01:01 | UPDATE | accounts | 賬戶余額+200 | 已記錄 |
002 | 09:01:01 | UPDATE | stock | 牛奶庫存-2 | 已記錄 |
特點(diǎn):
- 順序?qū)懭耄合窳魉~,效率高
- 記錄簡單:快速記錄交易信息
步驟2:臨時(shí)匯總(內(nèi)存)
在內(nèi)存中快速更新數(shù)據(jù):
賬戶 | 余額 |
現(xiàn)金 | 1200 |
商品 | 庫存數(shù)量 |
牛奶 | 98 |
特點(diǎn):
- 快速更新:客戶立即看到結(jié)果
- 數(shù)據(jù)在內(nèi)存中:但斷電會(huì)丟失
步驟3:持久化階段,定期刷盤
在以下時(shí)機(jī)將數(shù)據(jù)寫入硬盤:
- 營業(yè)員交接班時(shí)
- 系統(tǒng)空閑時(shí)
- 速記本快寫滿時(shí)
- 固定時(shí)間間隔
Redo Log 工作流程圖:

三、回滾日志(Undo Log):數(shù)據(jù)庫的"后悔藥"
1. 場景:超市的退貨處理
傳統(tǒng)退貨方式(沒有Undo Log):
顧客:我要退剛買的牛奶
小徐:抱歉,我們沒記錄原價(jià),不知道該退多少錢...
現(xiàn)代方式(使用Undo Log)
顧客:我要退剛買的牛奶
小徐:好的,讓我查看下交易記錄
- 找到原始購買記錄
- 確認(rèn)購買價(jià)格是100元
- 確認(rèn)庫存狀態(tài)
- 可以安全退貨
2. Undo Log 工作原理詳解
場景:用戶下單扣款
// 顧客購買2箱牛奶
UPDATE accounts SET balance = balance + 200; // 收款200元
UPDATE stock SET quantity = quantity - 2; // 庫存減2交易記錄階段(Undo Log記錄)
編號(hào) | 時(shí)間 | 表名 | 修改前數(shù)據(jù) | 修改后數(shù)據(jù) | 回滾指針 |
T001 | 09:01:01 | accounts | balance=1000 | balance=1200 | -> T000 |
T002 | 09:01:01 | stock | quantity=100 | quantity=98 | -> T001 |
T003 | 09:05:30 | accounts | balance=1200 | balance=1000 | -> T002 |
T004 | 09:05:30 | stock | quantity=98 | quantity=100 | -> T003 |
數(shù)據(jù)版本鏈(MVCC實(shí)現(xiàn)):
牛奶庫存記錄的版本鏈:
+-------------------------+
| 當(dāng)前版本:98箱 |
| 交易號(hào):T002 |
+-------------------------+
↓
+-------------------------+
| 上一版本:100箱 |
| 交易號(hào):T001 |
+-------------------------+
↓
+-------------------------+
| 初始版本:100箱 |
| 交易號(hào):T000 |
+-------------------------+Undo Log 工作流程圖:

3. Undo Log的兩大作用
(1) 支持事務(wù)回滾
- 記錄數(shù)據(jù)修改前的狀態(tài)
- 支持出錯(cuò)時(shí)回滾
- 保證事務(wù)原子性
(2) 實(shí)現(xiàn)MVCC(多版本并發(fā)控制)
- 不同事務(wù)看到不同版本的數(shù)據(jù)
- 提高并發(fā)性能
- 避免加鎖帶來的性能問題
四、二進(jìn)制日志(binlog):數(shù)據(jù)庫的"保險(xiǎn)箱"
1. 場景:連鎖超市的賬務(wù)管理
小徐是連鎖超市的總經(jīng)理,每天要處理這些數(shù)據(jù)管理問題:
場景一:商品管理
總店:上架100種新商品
┌─────────────────┐
│ 商品1: 牛奶 │
│ 商品2: 面包 │ ? 分店:一個(gè)個(gè)手動(dòng)添加?
│ ...100條記錄... │
└─────────────────┘
場景二:數(shù)據(jù)安全
┌─────────────────┐
│ 昨日銷售數(shù)據(jù) │ ? 系統(tǒng)崩潰,數(shù)據(jù)丟失!
└─────────────────┘
場景三:變更追蹤
老板:這個(gè)商品誰改的價(jià)格?
小徐:ˉ\_(ツ)_/ˉ 不知道...而有了Binlog(二進(jìn)制日志)后:
MySQL Binlog
├── 自動(dòng)同步
│ 總店改價(jià)格 ──? 所有分店秒級(jí)更新
│
├── 數(shù)據(jù)保護(hù)
│ 系統(tǒng)崩潰 ──? 從日志恢復(fù)數(shù)據(jù)
│
└── 操作追蹤
誰改了價(jià)格?──? 查看變更歷史2. Binlog的記錄格式:如何記錄數(shù)據(jù)變更?
讓我們看看Binlog是如何記錄數(shù)據(jù)變更的:
-- 一筆簡單的商品價(jià)格調(diào)整
UPDATE products SET price = 98 WHERE name = '牛奶';這條SQL語句在Binlog中有三種不同的記錄方式:
(1) STATEMENT格式:記錄SQL語句
# 直接記錄SQL
UPDATE products SET price = price * 0.9
WHERE category = '飲品';
優(yōu)勢:日志量小
風(fēng)險(xiǎn):可能導(dǎo)致主從不一致(比如NOW()函數(shù))(2) ROW格式:記錄數(shù)據(jù)變化
{
"before": {"id": 1, "name": "牛奶", "price": 100},
"after": {"id": 1, "name": "牛奶", "price": 90}
}
優(yōu)勢:數(shù)據(jù)準(zhǔn)確
特點(diǎn):日志量較大(3) MIXED格式:智能選擇
# 根據(jù)SQL類型自動(dòng)選擇格式
簡單UPDATE:使用STATEMENT
復(fù)雜函數(shù):使用ROW五、三大日志協(xié)同工作機(jī)制
以顧客購買2箱牛奶為例,操作內(nèi)容如下:
- 更新庫存(-2箱)
- 更新賬戶(+200元)
1. 兩階段提交工作流程
(1) 第一階段(Prepare):
記錄原始數(shù)據(jù)(Undo Log):
- 庫存:100箱
- 賬戶:1000元
更新內(nèi)存數(shù)據(jù):
- 庫存:98箱
- 賬戶:1200元
記錄操作狀態(tài)(Redo Log):
- 狀態(tài):準(zhǔn)備中
- 內(nèi)容:庫存-2,賬戶+200
(2) 第二階段(Commit):
記錄交易信息(Binlog):
- 時(shí)間:2025-04-14 09:00:00
- 操作:售出牛奶2箱,收款200元
標(biāo)記操作完成(Redo Log):
- 狀態(tài):已完成
詳細(xì)的執(zhí)行流程表:
步驟 | 操作 | 日志類型 | 內(nèi)容 | 狀態(tài) |
1 | 記錄原數(shù)據(jù) | Undo Log | 庫存=100,余額=1000 | 已記錄 |
2 | 更新內(nèi)存 | Buffer Pool | 庫存=98,余額=1200 | 已更新 |
3 | 預(yù)提交 | Redo Log | 更新操作記錄 | prepare |
4 | 記錄變更 | Binlog | 交易詳細(xì)信息 | 已寫入 |
5 | 最終提交 | Redo Log | 更新操作記錄 | commit |
兩階段提交流程圖:

六、總結(jié)
通過本文,我們深入了解了MySQL日志系統(tǒng)的核心內(nèi)容:
三大日志的作用與原理:
- Redo Log:確保數(shù)據(jù)持久性,像草稿紙,記錄每一步操作。
- Undo Log:支持事務(wù)回滾,類似價(jià)格標(biāo)簽,隨時(shí)可以撤銷錯(cuò)誤。
- Binlog:用于數(shù)據(jù)復(fù)制和恢復(fù),猶如記賬本,記錄所有交易歷史。
在數(shù)據(jù)庫的世界里,日志不僅是記錄,更是保障數(shù)據(jù)安全與一致性的基石。希望這些內(nèi)容能幫助你更深入地理解MySQL日志系統(tǒng)的關(guān)鍵作用!




















