前言

在 MySQL 中,有三大關鍵的 Log 機制:Undo LogRedo LogBin Log。這三個 Log 分別負責「交易回滾」、「當機恢復」、「主從複製」等核心功能。搞懂它們,就能理解 MySQL 如何保證資料的一致性持久性


歌曲

🎵 點擊展開觀看歌曲

三大 Log 一圖流快速理解

不同情境下的 Query 中,Log 扮演的角色


情境思考:哪些操作會用到 Log?

思考一下,以下兩個情境分別會用到哪些 Log?

1
2
3
4
5
-- 情境 1:SELECT 查詢
SELECT * FROM students WHERE id = 3;

-- 情境 2:資料異動
UPDATE students SET score = 90 WHERE id = 3;

答案揭曉

SELECT 查詢與 Log 的關係

SELECT 查詢不會寫入任何 Log,但根據隔離級別,可能會讀取 Undo Log 來實現 MVCC(多版本併發控制)。

Mysql 隔離級別是否讀取 Undo Log說明
Read Uncommitted❌ 不使用直接讀取最新資料,不在乎是否已 Commit
Read Committed✅ 使用每次查詢都讀取「已 Commit」的最新版本
Repeatable Read✅ 使用整個 Transaction 期間讀取「開始時」的快照版本
Serializable❌ 不使用透過鎖機制排隊執行,不需要讀取歷史版本

重點:SELECT 不會「寫入」Log,但可能「讀取」Undo Log

資料異動(INSERT / UPDATE / DELETE)

當「資料異動時」(Insert、Update、Delete)都會使用到這三個 Log

三個 Log 的分工如下:

Log 類型核心目的一句話說明
Undo Log回滾 + MVCCCommit 失敗時復原資料,同時支援其他 Transaction 讀取舊版本
Redo Log當機恢復Server 當機後,透過 Redo Log 恢復已 Commit 但尚未寫入磁碟的資料
Bin Log複製 + 備份主從複製的基礎,也用於時間點資料恢復

三大 Log 詳解

Undo Log(回滾日誌)

核心目的: Transaction 失敗時的「後悔藥」,同時支援 MVCC 讓其他 Transaction 讀取舊版本資料。

項目說明
目的1. Commit 失敗後復原資料
2. 支援 MVCC,讓其他 Transaction 讀取歷史版本
記錄內容舊值,例如 score 原本是 60,就記錄 score = 60
清空時機Insert:Commit 後可立即清空
Update/Delete:由 Purge Thread 判斷無 Transaction 需要後才清理
儲存位置Undo Tablespaces(undo_001undo_002 等檔案)

為什麼 Insert 和 Update/Delete 的清空時機不同?

1
2
3
4
5
Insert:新資料不存在「舊版本」,其他 Transaction 不需要讀取
→ Commit 後可以立即清空

Update/Delete:其他 Transaction 可能正在讀取舊版本(MVCC)
→ 必須等到沒有任何 Transaction 需要該版本後才能清理

Insert 的 Undo Log:Commit 後立即清空

Update/Delete 的 Undo Log:由 Purge Thread 異步清理,可能保留數小時

Redo Log(重做日誌)

核心目的: 實現 WAL(Write-Ahead Logging)= 先寫日誌再寫資料,確保 Server 當機後資料不丟失。

項目說明
目的避免 Commit 後、資料還沒寫入硬碟時 Server 當機導致資料遺失
記錄內容記錄「對哪個位置做了什麼修改」(例如:把第 5 頁的第 100 個位置改成 90)
清空時機循環寫入,滿了會從頭覆蓋舊的 Log
儲存位置ib_logfile0ib_logfile1 等檔案

WAL 是什麼?為什麼需要 Redo Log?

WAL = Write-Ahead Logging = 先寫日誌再寫資料

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
【沒有 Redo Log 的問題】

1. Client 執行 UPDATE
2. 修改 Buffer Pool 中的資料(在記憶體裡)
3. 回傳 Commit 成功 ✅
4. 背景程式「之後」才把資料寫入硬碟
5. 如果在步驟 4 之前當機... 💥
→ 記憶體資料消失,但 Client 已收到成功
→ 資料不見了!❌

【有 Redo Log 的保護】

1. Client 執行 UPDATE
2. 修改 Buffer Pool 中的資料(在記憶體裡)
3. 同時寫入 Redo Log 到硬碟(速度很快)
4. 回傳 Commit 成功 ✅
5. 如果在資料寫入硬碟之前當機... 💥
→ 重啟後讀取 Redo Log,把資料補回來
→ 資料恢復!✅

Redo Log 將「隨機寫入」轉換為「順序寫入」,提升效能

當機重啟後 MySQL 會自動透過 Redo Log 恢復資料

Bin Log(二進位日誌)

核心目的: 主從複製的基礎,也用於時間點資料恢復(Point-in-Time Recovery)。

項目說明
目的1. 主從複製(讀寫分離)
2. 時間點資料恢復
記錄內容記錄 SQL 語句或每一列的變化(取決於設定)
清空時機不清空!追加寫入,檔案滿了換新檔案
儲存位置mysql-bin.000001mysql-bin.000002

Redo Log vs Bin Log 差異

項目Redo LogBin Log
層級InnoDB 引擎層MySQL Server 層
記錄類型記錄「頁面哪裡改了什麼」記錄「SQL 語句或資料變化」
用途當機恢復(MySQL 重啟時自動讀取並恢復)主從複製 + 時間點恢復(需 DBA 手動執行 mysqlbinlog 指令)
寫入時機Transaction 執行中持續寫入Commit 時一次寫入
生命週期循環覆蓋持續追加

Redo Log:InnoDB 的「保險箱」,保護單機資料

Bin Log:MySQL 的「日記本」,支援複製與備份


三大 Log 能否被關閉?

重要觀念: 並非所有 Log 都可以被設定開關!

Log 類型能否關閉說明
Undo Log❌ 無法關閉InnoDB 核心機制,Transaction 回滾與 MVCC 必須依賴它
Redo Log❌ 無法關閉InnoDB 核心機制,WAL 策略與當機恢復必須依賴它
Bin Log✅ 可以關閉透過 log_bin 參數設定開關(預設在 MySQL 8.0+ 為開啟)
1
2
3
4
5
6
-- 查看 Bin Log 是否開啟
SHOW VARIABLES LIKE 'log_bin';

-- 關閉 Bin Log(需在 my.cnf 設定後重啟)
-- [mysqld]
-- skip-log-bin

為什麼只有 Bin Log 可以關閉?

  • Undo Log / Redo Log:屬於 InnoDB 儲存引擎層,是保證 ACID 特性的核心機制,關閉會破壞資料一致性
  • Bin Log:屬於 MySQL Server 層,主要用於主從複製與備份,單機環境下不需要複製功能時可以關閉

MySQL 完整架構圖

MySQL 更詳細的運作原理圖


架構圖中的其他知識點補充

Change Buffer

目的: 減少 Disk I/O,針對非唯一索引的寫入進行優化。

項目說明
對象Non-Unique 的 Secondary Index
原理將變更先暫存在 Buffer,之後再批次合併寫入
預設狀態MySQL 5.5~8.3:開啟
MySQL 8.4:關閉
MySQL 9.0+:開啟
適用時機HDD 環境下效益較大,SSD 環境可考慮關閉

什麼是非唯一索引(Non-Unique Index)?

索引類型分類:

  • 唯一索引(Unique Index):包含 Primary Key 與 Unique Key,確保欄位值不重複
  • 非唯一索引(Non-Unique Index):一般的 Secondary Index,允許欄位值重複

延伸思考:為什麼只有「非唯一索引」才能使用 Change Buffer?

核心原因: 唯一性檢查需要立即讀取資料頁,無法延遲寫入。

1
2
3
4
5
6
7
8
9
10
【唯一索引的寫入流程】
1. INSERT INTO students (id, email, score) VALUES (1, '[email protected]', 90);
2. 必須先檢查 email 是否已存在 → 需要讀取索引頁
3. 既然已經讀取了,就直接寫入,不需要 Change Buffer

【非唯一索引的寫入流程】
1. INSERT INTO students (id, email, score) VALUES (1, '[email protected]', 90);
2. score 欄位允許重複,不需要檢查 → 不用讀取索引頁
3. 將變更暫存在 Change Buffer,之後批次寫入
4. 減少隨機 I/O,提升效能

非唯一索引:可延遲寫入,減少 Disk I/O

唯一索引:必須立即檢查,無法使用 Change Buffer

Background Threads

目的: 職責拆分,適應高併發場景。

Thread 類型職責
IO Threads處理讀寫 I/O 請求
Purge Thread清理已不需要的 Undo Log
Page Cleaner將修改過的資料寫入硬碟
Log Writer將 Redo Log 從記憶體寫入硬碟
1
2
-- 查看當前 Thread 設定
SHOW VARIABLES LIKE 'innodb_%_threads';

範例輸出:

VariableValue
innodb_ddl_threads4
innodb_log_writer_threadsON
innodb_parallel_read_threads4
innodb_purge_threads1
innodb_read_io_threads5
innodb_write_io_threads4

Log Buffer 監控指令

1
2
3
4
5
6
7
8
9
10
11
-- ========== Binlog Buffer ==========
SHOW STATUS LIKE 'Binlog_cache_use'; -- 使用緩存的次數
SHOW STATUS LIKE 'Binlog_cache_disk_use'; -- 緩存不夠用而寫入臨時檔的次數
SHOW VARIABLES LIKE 'binlog_cache_size'; -- 緩存大小設定

-- ========== Redo Log Buffer ==========
SHOW VARIABLES LIKE 'innodb_log_buffer_size'; -- Buffer 大小
SHOW STATUS LIKE 'Innodb_log_waits'; -- 因 Buffer 不夠而等待的次數

-- ========== Undo Log / Buffer Pool ==========
SHOW ENGINE INNODB STATUS; -- 查看整體狀況

調優建議:

  • 如果 Binlog_cache_disk_use 很高 → 增加 binlog_cache_size
  • 如果 Innodb_log_waits 很高 → 增加 innodb_log_buffer_size

Adaptive Hash Index(AHI)

目的: 透過 Hash Map 加速熱點資料的查詢。

項目說明
原理MySQL 自動為頻繁查詢的資料建立 Hash 索引
預設狀態MySQL 8.0:開啟
MySQL 8.4+:關閉
適用場景有明顯熱點資料的 OLTP 場景
1
2
3
4
-- 查看 AHI 狀態
SHOW VARIABLES LIKE 'innodb_adaptive_hash_index';

-- 補充說明:主要針對熱點資料使用,只是現在高併發場景下的熱點資料都會搭配 NoSQL DB 來處理,AHI 再使用則佔用 Buffer,也需要考慮到 Lock 等各類問題,所以官方後續預設就設為關閉。

延伸比較:其他資料庫的 Log 機制

了解 MySQL 的三大 Log 後,我們來看看其他資料庫是如何設計的。


PostgreSQL:單一 WAL 取代三大 Log

PostgreSQL 架構流程圖

PostgreSQL 沒有 Undo Log、Redo Log、Bin Log! 它使用單一的 WAL(Write-Ahead Log) 來處理所有功能。

項目MySQLPostgreSQL
回滾機制Undo LogWAL + MVCC(舊版本保留在原表)
當機恢復Redo LogWAL
複製機制Bin LogWAL
連線模型多線程(Thread)多進程(Process)
緩存管理Buffer Pool(InnoDB 獨立管理)Shared Buffer(與 OS 共同管理)

MySQL vs PostgreSQL 的 Thread/Process 差異

MySQL 的風險: 如果某個 Thread 因為爛 SQL 導致記憶體飆升,觸發 OS OOM Killer(Linux),整個 MySQL Process 會被終止,影響所有連線。

PostgreSQL 的優勢: 每個連線是獨立 Process,單一連線出問題不會影響其他連線,但代價是資源消耗較高。


Redis:NoSQL 緩存的標配首選

Redis 架構流程圖

Redis 是現代應用中最常見的緩存解決方案,資料儲存在記憶體。

項目MySQL(OLTP)Redis(Cache)
主要儲存位置硬碟(Disk)記憶體(Memory)
讀寫速度毫秒級(ms)微秒級(μs)
持久化機制Redo Log 確保 Commit 後資料不丟失RDB 快照 + AOF 日誌(7.0+ 預設啟用)
適合場景持久化業務資料高頻讀取的熱點資料緩存

為什麼 Redis 這麼快?

核心原因: 記憶體的讀寫速度是硬碟的 10 萬倍以上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
【不同儲存媒介的讀取速度比較】

┌─────────────┬───────────────────┬─────────────────┐
│ 儲存媒介 │ 隨機讀取延遲 │ 相對速度 │
├─────────────┼───────────────────┼─────────────────┤
│ 記憶體 RAM │ ~100 ns │ ⭐ 基準(1x) │
│ SSD(NVMe) │ ~10-100 μs │ 慢 100~1000x │
│ SSD(SATA) │ ~100-500 μs │ 慢 1000~5000x │
│ HDD │ ~5-10 ms │ 慢 50000~100000x│
└─────────────┴───────────────────┴─────────────────┘

ns = 奈秒(10^-9 秒)
μs = 微秒(10^-6 秒)
ms = 毫秒(10^-3 秒)

Redis 將資料存在記憶體,讀取延遲僅需 ~100 ns

傳統 HDD 讀取需要 ~10 ms,差距高達 10 萬倍


Redis 的執行緒模型

常見誤解: 「Redis 是單執行緒」——這個說法只對一半!

版本執行模型說明
Redis < 6.0純單執行緒網路 I/O 和命令執行都在同一個執行緒
Redis ≥ 6.0多執行緒 I/O網路 I/O 改用多執行緒處理,命令執行仍是單執行緒
1
2
3
4
5
6
7
8
9
10
11
12
13
【Redis 6.0+ 執行緒模型】

┌─────────────────┐
│ 主執行緒 │
│ (命令執行) │ ← 單執行緒,無 Lock 機制
└────────┬────────┘

┌──────────────┼──────────────┐
│ │ │
┌─────┴─────┐ ┌─────┴─────┐ ┌─────┴─────┐
│ I/O Thread│ │ I/O Thread│ │ I/O Thread│
│ 讀取 │ │ 讀取 │ │ 寫入 │
└───────────┘ └───────────┘ └───────────┘

命令執行單執行緒:不需要 Lock,避免競爭條件(Race Condition)

網路 I/O 多執行緒:提升高併發場景下的吞吐量


Redis 的持久化機制:RDB vs AOF

相較於 MySQL 的三大 Log,Redis 使用 RDBAOF 兩種機制來解決「當機後資料恢復」的問題。

RDB(Redis Database)

原理: 定期將 Redis 記憶體中的資料「拍快照」,完整寫入 .rdb 檔案。

1
2
3
4
5
6
7
8
9
【RDB 運作流程】

T1: Redis 正常運作中,資料都在記憶體
T2: 觸發 RDB(手動 SAVE 或自動 BGSAVE)
T3: Fork 子進程,將記憶體資料寫入 dump.rdb
T4: 快照完成 ✅
...
T5: Redis 當機!💥
T6: 重啟後載入 dump.rdb,資料恢復到 T4 的狀態
項目說明
檔案格式二進位壓縮格式(.rdb)
觸發方式手動執行 SAVE/BGSAVE 指令,或在 redis.conf 設定條件自動觸發(如 save 900 1 表示 900 秒內有 1 次寫入就觸發)
還原速度快(直接載入快照)

SAVE vs BGSAVE 差異
指令執行方式說明
SAVE同步(阻塞)Redis 主執行緒直接執行快照,期間無法處理任何請求
BGSAVE背景(非阻塞)Fork 一個子進程來執行快照,主執行緒繼續處理請求
1
2
3
4
5
【SAVE 的問題】
執行 SAVE → Redis 主執行緒阻塞 → 所有 Client 都在等待... ⏳

【BGSAVE 的做法】
執行 BGSAVE → Fork 子進程寫入 RDB → 主執行緒繼續處理請求 ✅

生產環境幾乎都用 BGSAVE,避免服務中斷。SAVE 只適合在維護時段或測試環境使用。


什麼是 Fork?

fork() 是 Linux/Unix 的系統呼叫,用來「複製」當前進程,產生一個幾乎一模一樣的子進程。子進程會繼承父進程當下的記憶體狀態。

為什麼 BGSAVE 要用「子進程」而不是「新執行緒」?

  • 執行緒共享記憶體,寫入快照時若主執行緒修改資料,會導致快照內容不一致
  • 子進程透過 Copy-on-Write(寫時複製) 機制,取得當下記憶體的「快照副本」,不受主進程後續修改影響

優點 1:儲存完整快照,適合備份與災難恢復

優點 2:還原速度快,適合大量資料恢復

缺點 1:兩次快照之間若當機,中間的資料會遺失

缺點 2:Fork 子進程時需複製記憶體頁表,資料量大時會消耗 CPU 並造成短暫卡頓

AOF(Append-Only File)

原理: 每次執行寫入命令(SET、DEL 等),都追加記錄到 .aof 日誌檔案。

1
2
3
4
5
6
7
8
9
【AOF 運作流程】

T1: 執行 SET user:1 "John"
T2: 命令追加到 appendonly.aof
T3: 執行 SET user:2 "Jane"
T4: 命令追加到 appendonly.aof
...
T5: Redis 當機!💥
T6: 重啟後「重播」aof 中的所有命令,資料完整恢復 ✅
項目說明
檔案格式純文字格式(.aof),可直接 cat 查看
同步策略always(每條命令)/ everysec(每秒)/ no(由 OS 決定)
還原速度較慢(需逐行重播命令)

AOF 同步策略設定

redis.conf 中透過 appendfsync 參數設定:

1
2
3
4
5
6
7
8
9
# redis.conf

# 開啟 AOF
appendonly yes

# 同步策略(三選一)
appendfsync always # 每條命令都同步寫入硬碟
appendfsync everysec # 每秒同步一次(預設值,推薦)
appendfsync no # 由作業系統決定何時寫入
策略資料安全性效能說明
always⭐⭐⭐ 最高最慢每條命令都 fsync,最多丟失 1 條命令
everysec⭐⭐ 中等平衡每秒 fsync 一次,最多丟失 1 秒資料(推薦
no⭐ 最低最快由 OS 決定(通常 30 秒),可能丟失大量資料

生產環境推薦使用 everysec:在效能與資料安全之間取得平衡,最多只會丟失 1 秒內的資料。


優點 1:資料幾乎不遺失(設定 always 可確保每條命令都記錄)

優點 2:純文字格式,方便 Debug 和人工檢視

缺點 1:檔案比 RDB 大(記錄每一條命令)

缺點 2:還原速度較慢(需逐行執行命令)

RDB + AOF 混合持久化

Redis 4.0+ 推薦做法: 結合 RDB 和 AOF 的優點,這也是目前商業環境的主流配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
【混合持久化運作原理】

┌─────────────────────────────────────────┐
│ AOF 檔案結構 │
├─────────────────────────────────────────┤
│ ┌─────────────────────────────────┐ │
│ │ RDB 快照(二進位) │ │ ← 基礎資料(快速載入)
│ └─────────────────────────────────┘ │
│ ┌─────────────────────────────────┐ │
│ │ AOF 增量命令(快照後的變更) │ │ ← 增量資料(完整性)
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────┘

還原流程:
1. 先載入 RDB 快照 → 快速恢復大部分資料
2. 再重播 AOF 增量 → 補齊快照後的變更
對比項目純 RDB純 AOF混合模式
資料完整性⭐⭐⭐⭐⭐⭐⭐⭐
還原速度⭐⭐⭐⭐⭐⭐
檔案大小⭐⭐⭐⭐⭐

混合模式結合兩者優點:還原快 + 資料完整


Redis vs MySQL 持久化機制對比

項目MySQLRedis
當機恢復Redo Log(WAL)RDB / AOF
交易回滾Undo Log❌ 不支援(無 Transaction 概念)
主從複製Bin LogRDB 傳輸 + 命令同步
資料保證ACID最終一致性

MongoDB:可持久化的 NoSQL 資料庫

MongoDB 架構流程圖

MongoDB 是一個 Document-based 的 NoSQL 資料庫,兼具「彈性 Schema」與「持久化儲存」的特性,適合快速迭代或者記憶體有限 Server 時候要做緩存資料的首選。

項目MySQL(OLTP)MongoDB(Document DB)
資料模型關聯式表格(Row)文件(Document / JSON)
Schema固定欄位,需 ALTER TABLE彈性 Schema,可隨時新增欄位
儲存位置硬碟 + Buffer Pool 快取硬碟 + WiredTiger Cache(另有純記憶體引擎可選)
適合場景強一致性交易、複雜 JOIN快速開發、彈性資料結構、大量讀寫

關於「純記憶體引擎」的說明:

  • MySQL 和 MongoDB 預設都是硬碟儲存 + 記憶體快取(MySQL 用 Buffer Pool,MongoDB 用 WiredTiger Cache)
  • MongoDB 額外提供 In-Memory Storage Engine,這是一個完全不寫硬碟的儲存引擎,所有資料只存在記憶體中(等同於實作 Redis)
  • 純記憶體引擎適合:Session 管理、即時快取、臨時運算等可接受資料遺失的場景
  • 重啟後資料會全部消失,因此不適合需要持久化的業務資料

如何啟用 MongoDB In-Memory Storage Engine:

1
2
3
4
5
6
7
8
9
# 啟動時指定使用純記憶體引擎
mongod --storageEngine inMemory --dbpath /data/inmemory

# 或在設定檔 mongod.conf 中設定
storage:
engine: inMemory
inMemory:
engineConfig:
inMemorySizeGB: 4 # 分配 4GB 記憶體給資料庫

注意事項:

  • In-Memory Engine 僅在 MongoDB Enterprise 版本提供

MongoDB 的 Log 機制

MongoDB 使用 JournalOplog 兩種 Log,功能與 MySQL 的 Redo Log 和 Bin Log 類似。

Log 類型功能對應 MySQL
Journal當機恢復(WAL 機制)Redo Log
Oplog主從複製(操作日誌)Bin Log
1
2
3
4
5
6
7
8
【MongoDB 寫入流程】

1. Client 發送寫入請求
2. 資料先寫入 Journal(WAL)
3. 修改記憶體中的資料
4. 回傳成功給 Client ✅
5. 背景將資料寫入硬碟
6. 若當機,重啟後透過 Journal 恢復資料

Journal:類似 MySQL 的 Redo Log,實現 WAL 機制保護資料

Oplog:類似 MySQL 的 Bin Log,記錄所有操作供 Replica Set 同步


為什麼選擇 MongoDB?

彈性 Schema:隨時新增欄位

傳統 MySQL 痛點: 新增欄位需要 ALTER TABLE,大表可能鎖表數小時。
MongoDB 解法: Document 結構天生支援彈性欄位,無需 Migration。

1
2
3
4
5
6
7
8
9
// MongoDB:同一個 Collection 中,Document 結構可以不同

// 舊資料
{ "_id": 1, "name": "John", "email": "[email protected]" }

// 新增欄位後的資料(無需 ALTER TABLE!)
{ "_id": 2, "name": "Jane", "email": "[email protected]", "phone": "0912345678" }

// 兩筆資料可以共存於同一個 Collection

優點:快速迭代,適合敏捷開發

缺點:缺乏 Schema 約束,資料一致性需靠應用層保證

儲存在硬碟:比 Redis 更經濟

Redis vs MongoDB 的定位差異: Redis 是「記憶體緩存」,MongoDB 是「持久化資料庫」。

對比項目RedisMongoDB
資料儲存位置記憶體(RAM)硬碟(Disk)
成本高(記憶體貴)低(硬碟便宜)
資料量上限受記憶體限制可儲存 TB 級資料
適合場景熱點資料緩存主要資料儲存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
【成本估算範例 - 2026 AWS Tokyo 區域】

儲存 100GB 資料(On-Demand 價格):
┌─────────────────────────────────────────────────────────────┐
│ Redis(AWS ElastiCache) │
│ • 節點:cache.r6g.4xlarge(105GB RAM) │
│ • 費用:約 $1,300~1,800 USD/月(約 NT$40,000~55,000) │
├─────────────────────────────────────────────────────────────┤
│ MongoDB(AWS DocumentDB) │
│ • 節點:db.r5.large + 100GB 儲存 │
│ • 費用:約 $260~400 USD/月(約 NT$8,000~12,000) │
└─────────────────────────────────────────────────────────────┘

→ 成本差距約 4~5 倍!

📌 價格來源:AWS ElastiCache / DocumentDB 官方定價(2025-2026)
https://aws.amazon.com/elasticache/pricing/
https://aws.amazon.com/documentdb/pricing/

MongoDB 適合:需要持久化、資料量大、預算有限的場景

Redis 適合:高頻讀取的熱點資料、Session 緩存、排行榜

原生分片:輕鬆水平擴展

MongoDB 原生支援 Sharding(分片),可以輕鬆將資料分散到多個節點。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
【MongoDB Sharding 架構】

┌─────────────┐
│ mongos │ ← 路由層
│ (Router) │
└──────┬──────┘

┌─────────────────┼─────────────────┐
│ │ │
┌────┴────┐ ┌────┴────┐ ┌────┴────┐
│ Shard 1 │ │ Shard 2 │ │ Shard 3 │
│ (A-H) │ │ (I-P) │ │ (Q-Z) │
└─────────┘ └─────────┘ └─────────┘
│ │ │
Replica Set Replica Set Replica Set

名詞定義

  • 讀寫分離:一主多從,擴展「讀取」能力(MySQL 內建支援)
  • Sharding:資料分散到多個獨立資料庫,擴展「寫入」能力與儲存容量

當單一主庫遇到寫入瓶頸資料量超過單機上限時,讀寫分離無法解決,需要 Sharding。

對比項目MySQLMongoDB
讀寫分離✅ 內建支援✅ Replica Set
Sharding(水平擴展)❌ 需額外工具(Vitess、ShardingSphere)✅ 原生支援
設定複雜度Sharding 需要高
自動 Rebalance

什麼是 Rebalance(資料再平衡)?
當某個 Shard 資料量過大或新增節點時,系統自動將部分資料搬移到其他節點,確保負載均勻分佈。MongoDB 內建此功能,MySQL Sharding 方案通常需要手動處理。

原生 Sharding 支援,無需第三方工具

自動資料平衡,新增節點後自動遷移資料


ClickHouse:OLAP 資料庫的不同設計

ClickHouse 架構流程圖

ClickHouse 是 OLAP 資料庫,設計理念與 OLTP(MySQL、PostgreSQL)完全不同。

項目MySQL(OLTP)ClickHouse(OLAP)
儲存方式Row-basedColumn-based
適合場景高併發小查詢海量資料分析
Query Cache8.0 後移除有(v23.1+,預設關閉)
執行方式一個連線 = 一個 Thread⚠️(需要特別注意)一個 SQL = 所有 CPU 一起處理
寫入方式直接寫入指定位置先寫臨時區,背景 Merge

ClickHouse 的核心特點

Column-based 儲存:分析查詢只需讀取相關欄位,速度極快

Vectorized 執行:批次處理資料,大幅提升運算效率

MergeTree 引擎:寫入先暫存,背景合併,避免隨機 I/O

不適合高併發:一個查詢會佔用所有 CPU

不適合頻繁更新:設計上偏向「追加寫入」


Milvus:向量資料庫的架構

Milvus 架構流程圖

Milvus 是向量資料庫,專門處理 AI Embedding 的相似度搜尋,與傳統資料庫的設計完全不同。

項目MySQL(OLTP)Milvus(Vector DB)
查詢方式精確查詢(WHERE id = 3)相似度查詢(找最像的 Top K)
索引類型B+ Tree、HashIVF_FLAT、HNSW 等向量索引
比對方式值是否相等向量距離(計算相似程度)
適合場景電商訂單、用戶資料推薦系統、語意搜尋、RAG

常見向量距離算法:

  • 歐式距離(L2):計算兩點間的直線距離,適合絕對位置相似度比較
  • 內積(IP):計算向量夾角的餘弦值乘以長度,適合推薦系統
  • 餘弦相似度(Cosine):只看方向不看長度,適合文字語意比較

思考:假設使用 MySQL 儲存向量,程式端需自行實作相似度計算,可行嗎?

1
2
3
4
5
6
7
8
9
10
// PHP + Laravel 為例
$this->jobInfoRepository->chunk(1000, function ($jobInfos) use ($embedding, &$results) {
foreach ($jobInfos as $jobInfo) {
// 每一筆都要計算餘弦相似度(cosineSimilarity 為自定義方法)
$jobInfo->similarity = $this->cosineSimilarity(
json_decode($jobInfo->embedding, true),
$embedding
);
}
});

可行,但是這種做法會產生以下問題:

  1. CPU 負擔大:每次查詢都要對所有資料計算相似度
  2. 網路頻寬大:需將所有向量資料從 DB 傳到應用層
  3. 效能低落:無法利用索引,時間複雜度為 O(n)(為什麼失效,請具體查看下面的「為什麼向量資料庫不能用 B+ Tree?」

延伸思考:為什麼向量資料庫不能用 B+ Tree?

維度災難(Curse of Dimensionality): 向量通常是 768 維甚至更高,傳統索引在高維空間下完全失效。

舉例來說,一個句子經過 Embedding 模型轉換後,會變成這樣的向量:

1
"今天天氣很好" → [0.123, -0.456, 0.789, 0.234, ..., -0.567]  // 共 768 個維度

B+ Tree 的排序邏輯是「單一維度比大小」,但向量有 768 個維度,無法決定誰大誰小:

向量 A向量 B誰比較大?
[0.1, 0.9, ...][0.8, 0.2, ...]第一維 B 大,第二維 A 大,無法排序

因此向量資料庫必須使用專門的索引結構(如 HNSW、IVF),透過「近似最近鄰搜尋(ANN)」來快速找到相似向量。


應用場景對照表

1
2
3
4
5
6
7
8
9
┌──────────────────┬──────────────────────────────────────────────────┐
│ 資料庫類型 │ 適合場景 │
├──────────────────┼──────────────────────────────────────────────────┤
│ OLTP(MySQL) │ 電商訂單、用戶資料、交易紀錄 │
│ OLAP(ClickHouse)│ 日誌分析、報表統計、用戶行為分析 │
│ Vector(Milvus) │ 語意搜尋、推薦系統、AI 聊天機器人知識庫檢索 │
└──────────────────┴──────────────────────────────────────────────────┘

備註:如果向量資料量不大,PostgreSQL 的 pgvector 擴充也足夠應付。

總結

三大 Log 的核心價值:

  • Undo Log:交易的「後悔藥」+ MVCC 的基礎
  • Redo Log:當機恢復的「保險箱」(WAL 策略)
  • Bin Log:主從複製的「傳輸帶」+ 時間點恢復的「時光機」
Log 類型一句話總結
Undo LogCommit 失敗能回滾,其他 Transaction 能讀舊版本
Redo Log先寫 Log 再寫資料,當機後能恢復
Bin Log主從複製靠它傳資料,備份恢復靠它回到過去

延伸閱讀

MySQL 官方文件:

相關文章: