本章目标:理解索引的价值与代价,掌握常见索引类型、建索引策略、
EXPLAIN分析方法,并能落地 SQL 优化。
没有索引时,数据库需要全表扫描(逐行比对);数据量一大,查询会明显变慢。
有索引后,数据库可通过有序结构快速定位目标数据,减少扫描行数。
graph LR
A[SQL 查询请求] --> B{是否有可用索引}
B -- 否 --> C[全表扫描]
B -- 是 --> D[索引定位]
C --> E[返回结果]
D --> E
收益:
WHERE、JOIN、ORDER BY、GROUP BY 查询性能成本:
INSERT / UPDATE / DELETE 时需要维护索引,写入变慢(department, status, salary)假设有员工表:
CREATE TABLE employees (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
department VARCHAR(30) NOT NULL,
status TINYINT NOT NULL DEFAULT 1,
salary DECIMAL(10,2) NOT NULL,
hire_date DATE NOT NULL
);CREATE INDEX idx_emp_department ON employees(department);CREATE UNIQUE INDEX uk_emp_name ON employees(name);CREATE INDEX idx_emp_dept_status_salary
ON employees(department, status, salary);SHOW INDEX FROM employees;
DROP INDEX idx_emp_department ON employees;联合索引 (a, b, c) 可命中的典型条件:
WHERE a = ?WHERE a = ? AND b = ?WHERE a = ? AND b = ? AND c = ?WHERE a = ? AND c = ?(部分场景可部分利用)较难利用索引的写法:
WHERE b = ? AND c = ?(缺少最左列 a)EXPLAIN 是分析 SQL 性能的入口。
EXPLAIN
SELECT id, name, salary
FROM employees
WHERE department = '研发' AND status = 1
ORDER BY salary DESC;重点关注列:
type:访问类型(越靠近 const/ref/range 越好,避免 ALL)key:实际使用的索引rows:预计扫描行数(越小越好)Extra:是否 Using filesort、Using temporarySELECT * 只查需要字段,减少回表和网络传输成本。
如订单表常按 user_id、status、created_at 查询,就优先考虑这些字段。
-- 不推荐:可能导致索引失效
WHERE DATE(created_at) = '2026-04-21';
-- 推荐:范围查询
WHERE created_at >= '2026-04-21 00:00:00'
AND created_at < '2026-04-22 00:00:00';字段是数字就传数字,字段是字符串就传字符串,避免索引失效。
大偏移量分页(LIMIT 100000, 20)代价高,可结合“覆盖索引 + 延迟关联”优化。
SELECT *
FROM employees
WHERE department = '研发'
AND status = 1
ORDER BY salary DESC;问题:
SELECT * 字段过多CREATE INDEX idx_emp_dept_status_salary
ON employees(department, status, salary);SELECT id, name, salary
FROM employees
WHERE department = '研发'
AND status = 1
ORDER BY salary DESC;EXPLAIN 对比优化前后 rows 与 type先通过二级索引找到主键,再回到主键索引取完整行数据。
查询字段全部在索引里,直接返回,不需要回表,性能更好。
不是。索引过多会拖慢写入、占空间、增加维护复杂度。
orders(user_id, status, created_at) 设计一个合理联合索引。EXPLAIN 分析一条你自己的业务查询 SQL。SELECT * 为什么在大表中通常不推荐。