存儲引擎對比:MyISAM vs InnoDB
| 維度 | MyISAM | InnoDB (默認 5.5+) |
| 事務支持 | ❌ 無 | ✅ ACID、行鎖、MVCC |
| 外鍵 | ❌ 無 | ✅ 支持 |
| 鎖粒度 | 表鎖 | 行鎖 (意向鎖 + 記錄鎖 + 間隙鎖) |
| 索引結構 | 非聚簇 (數據文件、索引文件分離) | **聚簇索引** (主鍵即數據文件) |
| 全文索引 | 5.6+ 支持 | 5.6+ 支持 |
| 崩潰恢復 | 無 (需 myisamchk 修復) | Redo/Undo Log 自動恢復 |
| 適用場景 | 只讀/讀多寫少、全文搜索、非關鍵日志 | **核心業務、高併發、數據一致性要求高** |
InnoDB 索引架構:聚簇 vs 二級
聚簇索引 (Clustered Index / Primary Key)
- **葉子節點 = 完整行數據** (而非指針)。
- 表數據按主鍵順序物理存儲 (邏輯順序,頁分裂維護)。
- **查詢走主鍵 = 直接命中數據,無回表**。
- 主鍵設計原則:**單調遞增 (自增/雪花 ID)**,減少頁分裂、碎片、索引體積。
二級索引 (Secondary Index / Non-clustered)
- 葉子節點存 **(索引列值, 主鍵值)**。
- 查詢流程:**遍歷二級索引 B+ 樹 → 獲取主鍵值 → 回表查聚簇索引 → 返回行數據**。
- **覆蓋索引:** 查詢列全包含在二級索引中 → **無需回表**,性能躍升。
- **代價:** 每個二級索引佔用額外空間,寫入需維護多棵 B+ 樹。
MyISAM 索引:全非聚簇
- 數據文件 `.MYD` 按插入順序存儲 (堆表)。
- 索引文件 `.MYI` 葉子節點存 **數據文件物理偏移量 (行指針)**。
- 主鍵/二級索引結構相同,無「回表」概念,直接按偏移讀數據文件。
業務負載分類:OLTP vs OLAP
| 特徵 | OLTP (聯機事務處理) | OLAP (聯機分析處理) |
| 典型操作 | 增刪改查、單行/少量行 | 大範圍掃描、聚合、複雜連接 |
| 響應要求 | 毫秒級 | 秒~小時級 |
| 數據模型 | 高度正規化 (3NF) | 星型/雪花模型、反正規化 |
| 索引策略 | 精準點查索引 | 列存、位圖索引、物化視圖 |
| 代表系統 | 訂單、支付、用戶中心 | 數據倉庫、報表、BI |
聯接算法演進
1. Nested-Loop Join (NLJ) - 基礎
FOR each row in outer_table:
FOR each row in inner_table:
IF join_condition: OUTPUT
- 複雜度 O(M×N),極慢。
- **有索引優化:** 內表走索引查找,近似 O(M × log N) (索引高度)。
2. Block Nested-Loop Join (BNL) - MySQL 5.6+ 默認
- 將外表分批讀入 **Join Buffer** (默認 256KB,`join_buffer_size`)。
- 內表掃描一次,與 Buffer 中所有行比較。
- **掃描內表次數 = ceil(外表行數 / Buffer容量)**,大幅減少內表掃描。
- 無索引時極大提升,有索引時優化器可能仍選 NLJ。
3. Hash Join - MySQL 8.0.18+
- **僅支持等值連接 (`=`)**,不支持範圍/模糊。
- 構建階段:外表數據讀入 Join Buffer → **構建哈希表**。
- 探測階段:掃描內表 → 計算哈希 → 探測匹配。
- 複雜度 O(M+N),大表連接**數量級快於 BNL**。
- 控制:`optimizer_switch='hash_join=on/off'`。
算法選擇決策樹
- 有合適索引 → **Index Nested-Loop Join** (最優)。
- 無索引、等值連接、大表 → **Hash Join** (8.0.18+)。
- 無索引、非等值/小表 → **Block Nested-Loop Join**。
- 強制指定:`SELECT /*+ MAX_EXECUTION_TIME(1000) */` / `optimizer_hints` (8.0+)。
實戰測試代碼片段
CREATE TABLE myorder (
id INT NOT NULL AUTO_INCREMENT,
userid INT NOT NULL,
orderdate DATE,
comein INT DEFAULT 0,
comeout INT DEFAULT 0,
PRIMARY KEY (id)
);
INSERT INTO myorder VALUES (NULL, 11123940, '2014-05-10', 0, 50);
-- 分組聚合查詢
SELECT userid, orderdate, comein - comeout AS rest
FROM myorder
GROUP BY userid, orderdate;
-- 建立覆蓋索引優化分組
CREATE INDEX idx_order_cover ON myorder (userid, orderdate, comein, comeout);
-- 執行計劃分析
EXPLAIN SELECT userid, orderdate, comein - comeout AS rest
FROM myorder
GROUP BY userid, orderdate;