上周排查一个生产环境问题时,发现某个报表查询竟然要跑8分钟。查看执行计划才发现是全表扫描了2000万条记录,加上错误的连接顺序导致性能雪崩。这让我想起刚入行时踩过的坑——很多开发者把SQL当作黑盒子,只关心结果不管过程。实际上,SQL优化是性价比最高的性能提升手段,往往一个索引就能让查询从分钟级降到毫秒级。
今天我们就用真实案例,拆解从执行计划解读到索引设计的完整优化路径。无论你是刚接触数据库的新手,还是想系统提升优化能力的老手,这套方法都能直接套用到你的项目中。我们会用MySQL 8.0演示,但原理通用所有关系型数据库。
拿到慢查询后,第一件事就是用EXPLAIN看执行计划。这个命令会展示MySQL如何执行你的SQL,就像给查询做CT扫描。关键要关注这几个字段:
sql复制EXPLAIN FORMAT=JSON
SELECT * FROM orders o
JOIN users u ON o.user_id = u.id
WHERE u.status = 'active'
AND o.create_time > '2023-01-01';
输出中的access_type字段特别重要:
ALL:全表扫描(红色警报)index:全索引扫描range:索引范围扫描ref/eq_ref:索引等值查询(理想状态)最近帮客户优化过一个典型案例:某电商平台的订单搜索接口超时。EXPLAIN显示对order_item表进行了ALL扫描,仅这一步就处理了1200万行数据。
这几个信号出现时就要警惕了:
去年优化过一个统计报表,就因为GROUP BY没走索引导致生成400MB临时表,把服务器内存撑爆。后来通过创建(category, date)的联合索引,执行时间从47秒降到0.8秒。
所有建议"加索引"的文章都没告诉你:索引是把双刃剑。理解B+树原理才能用好它。比如你知道为什么推荐用自增主键吗?
因为B+树的叶子节点是双向链表,自增ID插入时只需追加,而UUID会导致频繁的分裂和页合并。我们做过压测:同样500万数据,UUID主键的写入TPS比自增ID低38%。
这是最容易被误解的规则。假设有联合索引(a,b,c):
sql复制WHERE a=1 AND b>2 AND c=3 -- 只能用a,b
WHERE b=2 AND c=3 -- 用不上索引
WHERE a=1 AND c=3 -- 只用a
曾见过同事创建了(user_id, status)索引,但查询只用status条件,结果索引完全失效。正确的做法是把高频查询字段放在左边。
选择性=不重复值/总行数。给性别这种低选择性字段加索引通常没用。有个经验公式:
code复制选择性 > 0.2 适合建索引
选择性 < 0.1 不建议建
但要注意特例:某物流系统用(province, city)做联合索引,虽然province只有30多个值,但加上city后选择性骤增,查询速度提升20倍。
LIMIT 10000, 10这种深分页为什么慢?因为它要先读取10010条再抛弃前10000条。我们优化过的一个案例:
sql复制-- 原始写法(执行2.4秒)
SELECT * FROM articles
WHERE user_id=123
ORDER BY id DESC
LIMIT 100000, 20;
-- 优化方案(0.01秒)
SELECT * FROM articles
WHERE user_id=123 AND id < last_seen_id
ORDER BY id DESC
LIMIT 20;
配合前端记录最后一条记录的ID,性能提升240倍。
最近重构的一个复杂查询,通过调整JOIN顺序+索引覆盖,从11秒降到0.3秒。关键是把只有10条记录的维度表作为驱动表。
WHERE YEAR(create_time)=2023!=, NOT INuser_id='123'(本是int)LIKE '%abc'WHERE SUBSTRING(name,1,3)='abc'上周刚解决一个BUG:WHERE mobile=13800138000没走索引,因为mobile字段是varchar类型。
这几个参数调整后性能立竿见影:
ini复制innodb_buffer_pool_size = 12G # 建议设为内存的70%
innodb_io_capacity = 2000 # SSD建议2000+
innodb_stats_on_metadata = OFF # 避免自动更新统计信息
某客户从默认配置优化后,TPS从800提升到3500。但要特别注意:修改配置后一定要用sysbench做基准测试。
去年优化的一个真实电商系统:
WHERE create_time BETWEEN ? AND ?条件(user_id, status)(create_time, user_id)联合索引FORCE INDEX确保走新索引关键点在于理解业务场景——这个导出功能总是按时间范围筛选,但原有索引完全没考虑这个模式。
最近用pt-index-usage发现某系统有23个冗余索引,删除后写入性能提升15%。但切记:删除前要确认这些索引确实不在生产查询中使用。