“给字段加索引”并不是可靠的优化方法。正确流程是:固定查询与数据规模,读取执行计划,根据过滤与排序顺序设计索引,再用实际执行数据验证。
练习建议:只在本地测试库操作。
EXPLAIN ANALYZE会真正执行语句,生产环境必须先评估成本。
一、建立可复现的查询场景
CREATE TABLE orders (
id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
customer_id BIGINT UNSIGNED NOT NULL,
status VARCHAR(20) NOT NULL,
total DECIMAL(10,2) NOT NULL,
created_at DATETIME NOT NULL,
INDEX idx_created_at (created_at)
);
SELECT id, total, created_at
FROM orders
WHERE customer_id = 42 AND status = 'paid'
ORDER BY created_at DESC
LIMIT 20;
测试数据应覆盖多个客户、状态和日期。数据量太小会让优化器倾向全表扫描,不能代表真实业务。
二、先阅读 EXPLAIN,不急着建索引
EXPLAIN FORMAT=TREE
SELECT id, total, created_at
FROM orders
WHERE customer_id = 42 AND status = 'paid'
ORDER BY created_at DESC
LIMIT 20;
传统表格输出中重点观察:
type:ALL通常表示全表扫描;ref、range往往更有选择性。possible_keys与key:候选索引和最终采用的索引。rows:预计扫描行数,不是返回行数。Extra:关注Using filesort、Using temporary等额外成本。
三、按等值过滤与排序设计联合索引
CREATE INDEX idx_orders_customer_status_created
ON orders (customer_id, status, created_at DESC);
前两列对应等值过滤,第三列承接排序。联合索引不是字段集合,而是有顺序的查找结构;如果查询只按 status 过滤,这个索引未必合适。
四、用实际执行计划验证
EXPLAIN ANALYZE
SELECT id, total, created_at
FROM orders
WHERE customer_id = 42 AND status = 'paid'
ORDER BY created_at DESC
LIMIT 20;
比较优化前后的实际行数、循环次数和耗时。测试至少包含:高频客户、低频客户、没有结果的客户。只优化一个理想参数,可能造成错误结论。
五、检查索引成本与回滚
SHOW INDEX FROM orders;
-- 确认新索引无收益时再删除
DROP INDEX idx_orders_customer_status_created ON orders;
索引会占磁盘空间,也会增加 INSERT、UPDATE、DELETE 的维护成本。上线前记录基线,上线后观察慢查询、写入延迟和索引大小。
验证清单
- 查询语义和返回结果没有变化。
- 执行计划使用目标索引。
- 扫描行数和实际耗时在多组参数下下降。
- 没有重复或长期未使用的索引。
- DDL 变更具备维护窗口和回滚方案。
常见误区
rows 变小就一定更快
不一定。还要观察随机 I/O、回表、排序、锁等待与缓存状态。
强制索引代替根因分析
数据分布会变化。优先修正索引与查询结构,只有充分验证后才考虑索引提示。