索引是提升查詢速度最關鍵工具。本文深度解析索引工作原理(B+Tree)、優缺點權衡、選擇策略(基數、前綴、最左前綴)、索引類型匹配(Hash vs B-Tree)、慢查詢日誌驅動優化,並對比 MyISAM/InnoDB/BDB 存儲引擎索引實現差異。
無索引表: 堆表,全表掃描 O(N)
有索引表: B+Tree 結構,索引項按鍵值有序排列,查找 O(log N)
-- 無索引:掃描所有行
SELECT * FROM ad WHERE company_num = 13;
-- 有索引:B+Tree 定位
CREATE INDEX idx_company ON ad(company_num);
-- 1. 二分查找/ B+Tree 遍歷定位 company_num=13 首條
-- 2. 連續讀取後續滿足條件的葉子節點
-- 3. 遇到 company_num=14 停止 (有序特性)
| 引擎 | 數據/索引存儲 | 特點 |
|---|---|---|
| MyISAM | 分離:.MYD (數據) + .MYI (索引),多索引共用 .MYI | 非聚簇,主鍵無特殊地位 |
| InnoDB | 共享表空間或獨立表空間,數據與索引同存 | 聚簇索引:主鍵即數據,二級索引存主鍵值 |
| BDB | 單文件存數據+索引 | 類 InnoDB,已淘汰 |
-- 三表關聯,各 1000 行
SELECT t1.i1, t2.i2, t3.i3
FROM t1, t2, t3
WHERE t1.i1 = t2.i2 AND t2.i2 = t3.i3;
-- 無索引:1000^3 = 10 億次組合嘗試
-- 有索引:掃描 t1 (1000) + t2 索引查找 (log N) + t3 索引查找 (log N)
-- 理論提速百萬倍
SELECT indexed_col FROM t 僅讀索引文件| 代價 | 影響 | 量化 |
|---|---|---|
| 寫入減速 | INSERT/UPDATE/DELETE 需維護所有索引 | 索引數 × 寫入量 × log N |
| 磁盤空間 | 每個索引佔用額外空間 | MyISAM: .MYI 膨脹; InnoDB: 表空間/獨立文件膨脹 |
| 優化器負擔 | 索引過多導致選擇錯誤 | 統計信息更新開銷 |
原則: 不需要的索引堅決不建,定期 pt-index-usage 清理零使用索引。
Cardinality / Rows > 0.3,主鍵 > 唯一 > 普通,枚舉/性別低區分度不建VARCHAR(255) 索引前 10-20 字符,INT < BIGINT(a,b,c) 實際支援 a/a,b/a,b,c,不可跳列(a,b,c) 勿再建 (a)/(a,b)| 特性 | Hash 索引 (MEMORY 默認) | B-Tree 索引 (InnoDB/MyISAM 默認) |
|---|---|---|
| 精確查找 (=, <=>) | ⭐⭐⭐⭐⭐ O(1) | ⭐⭐⭐⭐ O(log N) |
| 範圍查詢 | ❌ 不支援 | ⭐⭐⭐⭐⭐ 支援 |
| 排序/分組 | ❌ 不支援 | ⭐⭐⭐⭐⭐ 支援 |
| LIKE 'prefix%' | ❌ 不支援 | ⭐⭐⭐⭐ 支援 |
| 適用場景 | 純內存表、純精確查找 | 通用、持久化表 |
-- MEMORY 表指定 B-Tree
CREATE TABLE lookup (
id INT NOT NULL,
name CHAR(20),
PRIMARY KEY USING BTREE (id)
) ENGINE=MEMORY;
# 1. 開啟慢查詢日誌
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2 -- 秒
log_queries_not_using_indexes = 1
# 2. 分析工具
pt-query-digest /var/log/mysql/slow.log
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log
# 3. 關注指標
-- Query_time: 總耗時
-- Lock_time: 鎖等待
-- Rows_sent/Rows_examined: 返回/掃描行數比 (越低越好)
-- Query: 具體 SQL
# 4. 優化動作
-- 加索引、改 SQL、調參數、分表、引入緩存
YEAR(date)=2023 → date >= '2023-01-01')LIKE '%keyword%'` 改全文索引或 ESANALYZE TABLE 更新統計信息Handler_read_rnd_next (全表掃描計數) 異常增長