一、SQL索引基礎:數(shù)據(jù)庫的“目錄”系統(tǒng)
1.1 為什么需要SQL索引?
想象一本500頁的《現(xiàn)代漢語詞典》:
- 沒有目錄:要查“數(shù)據(jù)庫”一詞,需逐頁翻閱(全表掃描)
- 有目錄:先查“數(shù)”字起始頁(索引檢索),快速定位
當數(shù)據(jù)量達到百萬級時,SQL索引可將查詢速度提升幾十到幾百倍。
1.2 創(chuàng)建SQL索引的三種方式
-- 方式1:直接創(chuàng)建
CREATE INDEX idx_name ON users(email);
-- 方式2:修改表結(jié)構(gòu)
ALTER TABLE users ADD INDEX idx_name (email);
-- 方式3:建表時創(chuàng)建
CREATE TABLE users (
id INT PRIMARY KEY,
email VARCHAR(100),
INDEX idx_email (email) -- 普通索引
);
1.3 SQL索引管理命令
| | |
---|
| SHOW INDEX FROM users; | |
| DROP INDEX idx_email ON users; |
|
| SELECT ... FORCE INDEX(idx_name) | |
二、SQL索引類型詳解:數(shù)據(jù)庫的“多重目錄”
2.1 按數(shù)據(jù)結(jié)構(gòu)劃分(存儲引擎層實現(xiàn))
?? 重點對比:
-- B+樹索引支持的操作
SELECT * FROM users WHERE age > 25; -- 范圍查詢
SELECT * FROM users ORDER BY create_time; -- 排序
-- 哈希索引僅支持
SELECT * FROM users WHERE id = 10086; -- 精確匹配
2.2 按字段數(shù)量劃分
| | |
---|
單列索引 | | INDEX (email) |
聯(lián)合索引 | | INDEX (last_name, first_name) |
?? 聯(lián)合索引最左前綴原則:
CREATE INDEX idx_name_phone ON users(last_name, phone);
-- ? 生效場景
SELECT * FROM users WHERE last_name = '張';
SELECT * FROM users WHERE last_name = '張' AND phone='138****8000';
-- ? 失效場景
SELECT * FROM users WHERE phone = '138****8000';
2.3 按功能邏輯劃分(最常用分類)
| | |
---|
普通索引 | | ADD INDEX idx_name (name) |
唯一索引 | | ADD UNIQUE INDEX (email) |
主鍵索引 | | ADD PRIMARY KEY (id) |
全文索引 | | ADD FULLTEXT INDEX (content) |
空間索引 | | ADD SPATIAL INDEX (geom) |
主鍵索引 vs 唯一索引:
INSERT INTO users (id, email) VALUES (NULL, 'a@test.com');
-- 主鍵索引報錯:主鍵不能為NULL
-- 唯一索引允許:唯一索引列允許一個NULL值
2.4 按存儲方式劃分(InnoDB核心機制)
| | |
---|
聚簇索引 | 索引節(jié)點直接包含行數(shù)據(jù) | |
非聚簇索引 | | |
理解聚簇索引: 假設用戶表結(jié)構(gòu):
聚簇索引(主鍵索引):
- 葉子節(jié)點存儲:| id | name | email | age |
非聚簇索引(普通索引):
- 葉子節(jié)點存儲:| age | id |
當通過非聚簇索引查詢時:
SELECT name FROM users WHERE age = 30;
執(zhí)行路徑:age索引 -> 主鍵id -> 聚簇索引 -> 獲取數(shù)據(jù)行
三、模擬數(shù)據(jù)與應用示例(10萬行數(shù)據(jù)演示)
3.1 數(shù)據(jù)準備
CREATE TABLE products (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(200) NOT NULL,
price DECIMAL(10,2) NOT NULL,
category VARCHAR(50) NOT NULL,
description TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;
-- 插入10萬條產(chǎn)品數(shù)據(jù)(腳本略)
3.2 SQL索引效果對比實驗
場景1:無索引基礎查詢
-- 耗時約350ms
SELECT * FROM products WHERE category = 'electronics';
場景2:創(chuàng)建單列索引后
CREATE INDEX idx_category ON products(category);
-- 耗時降至8ms (提升44倍)
EXPLAIN SELECT * FROM products WHERE category='electronics';
-- 執(zhí)行計劃:type=ref, key=idx_category
場景3:聯(lián)合索引范圍查詢
CREATE INDEX idx_category_price ON products(category, price);
-- ? 高效查詢(使用索引)
SELECT * FROM products
WHERE category='books' AND price > 100;
-- ? 低效查詢(未用索引)
SELECT * FROM products WHERE price > 100;
場景4:覆蓋索引優(yōu)化
-- 原始查詢(需回表)
SELECT id, name FROM products WHERE category='furniture';
-- 創(chuàng)建覆蓋索引
CREATE INDEX idx_cover ON products(category, name, id);
-- 執(zhí)行計劃顯示"Using index"
EXPLAIN SELECT id, name FROM products WHERE category='furniture';
四、SQL索引使用注意事項
4.1 SQL索引的代價
4.2 SQL索引失效的六大場景
函數(shù)操作:
SELECT * FROM users WHERE UPPER(name) = 'JOHN'; -- 失效
SELECT * FROM users WHERE name = 'John'; -- 有效
隱式類型轉(zhuǎn)換:
-- phone是字符串類型
SELECT * FROM users WHERE phone = 13800138000; -- 失效
模糊查詢通配符開頭:
SELECT * FROM users WHERE name LIKE '%son'; -- 失效
SELECT * FROM users WHERE name LIKE 'Joh%'; -- 有效
OR連接非索引列:
-- age列無索引
SELECT * FROM users WHERE name='John' OR age=30; -- 失效
聯(lián)合索引跳過首列:
CREATE INDEX idx_name_phone ON users(name, phone);
SELECT * FROM users WHERE phone='13800138000'; -- 失效
數(shù)據(jù)傾斜優(yōu)化器棄用:
-- 90%數(shù)據(jù)category='electronics'
SELECT * FROM products WHERE category='electronics'; -- 可能全表掃描
4.3 SQL索引設計原則
- 高頻查詢優(yōu)先:WHERE/JOIN/ORDER BY/GROUP BY涉及的列
- 區(qū)分度高原則:選Cardinality值高的列(如身份證號比性別適合)
- 避免冗余索引:
INDEX(a,b) -- 已存在
INDEX(a) -- 冗余!
附錄:SQL索引學習地圖
graph TD
A[索引基礎] --> B[創(chuàng)建與管理]
A --> C[類型體系]
C --> D[數(shù)據(jù)結(jié)構(gòu)]
C --> E[字段數(shù)量]
C --> F[功能邏輯]
C --> G[存儲方式]
D --> H[B+樹 vs 哈希]
E --> I[聯(lián)合索引最左前綴]
F --> J[主鍵/唯一/全文]
G --> K[聚簇/非聚簇]
L[實戰(zhàn)應用] --> M[性能對比]
L --> N[覆蓋索引]
L --> O[失效場景]
閱讀原文:原文鏈接
該文章在 2025/9/1 12:07:06 編輯過