“系统怎么又卡了?”
“数据库 CPU 飙到 90% 了!”
“快看看是不是又有慢 SQL!”
在后端开发中,SQL 优化是每个开发者必须掌握的硬核技能。很多时候,我们写的 SQL 在开发环境跑得飞快,一上线遇到大数据量就“原形毕露”。
今天,我们不讲枯燥的索引原理,直接上 7 个真实的慢 SQL 案例,手把手教你如何分析和优化,让你的 SQL 性能提升 10 倍甚至 100 倍!
案例一:最左前缀法则失效 🚫
场景:用户表 user 有个联合索引 idx_name_age (name, age)。
❌ 慢 SQL:
SELECT * FROM user WHERE age = 20;
💥 原因分析:
联合索引遵循**“最左前缀法则”**。查询条件必须从索引的最左边列开始,否则索引失效。
这里直接查 age,跳过了 name,导致索引失效,全表扫描。
✅ 优化方案:
-
修改查询:如果业务允许,带上 name 条件。
SELECT * FROM user WHERE name = '张三' AND age = 20;
-
新建索引:如果经常单独查 age,给 age 单独建个索引。
案例二:索引列上做计算 🧮
场景:查询 2024 年注册的用户。
❌ 慢 SQL:
SELECT * FROM user WHERE YEAR(create_time) = 2024;
💥 原因分析:
在索引列上使用函数(YEAR())或进行计算(+ - * /),会导致数据库无法使用索引树进行查找,只能全表扫描。
✅ 优化方案:
把计算放到等号右边,或者改写成范围查询。
SELECT * FROM user WHERE create_time BETWEEN '2024-01-01 00:00:00' AND '2024-12-31 23:59:59';
案例三:隐式类型转换 🎭
场景:用户手机号 phone 字段是 varchar 类型,且建了索引。
❌ 慢 SQL:
SELECT * FROM user WHERE phone = 13800138000;
💥 原因分析:
查询条件里 138... 是数字类型,而数据库字段是字符串。MySQL 会自动把字符串转成数字进行比较,这相当于在索引列上做了函数操作,导致索引失效。
✅ 优化方案:
参数加上单引号,保持类型一致。
SELECT * FROM user WHERE phone = '13800138000';
案例四:深分页问题 📜
场景:查询第 100 万页的数据。
❌ 慢 SQL:
SELECT * FROM user LIMIT 1000000, 10;
💥 原因分析:
MySQL 需要先查出前 1000010 条记录,然后丢弃前 1000000 条,只取最后 10 条。这会产生大量的回表操作,性能极差。
✅ 优化方案:
延迟关联 (Late Row Lookups) :先通过覆盖索引查出 ID,再根据 ID 查详情。
SELECT t1.* FROM user t1
INNER JOIN (SELECT id FROM user LIMIT 1000000, 10) t2 ON t1.id = t2.id;
案例五:OR 查询导致索引失效 ⚖️
场景:查询 id = 1 或者 age = 20 的用户。id 有索引,age 没索引。
❌ 慢 SQL:
SELECT * FROM user WHERE id = 1 OR age = 20;
💥 原因分析:
OR 查询中,只要有一个条件列没有索引,那么涉及的索引都不会被用到,直接全表扫描。
✅ 优化方案:
使用 UNION 代替 OR。
(SELECT * FROM user WHERE id = 1)
UNION
(SELECT * FROM user WHERE age = 20);
案例六:SELECT * 的滥用 📦
场景:只需要用户的手机号,却查了所有字段。
❌ 慢 SQL:
SELECT * FROM user WHERE name = '张三';
💥 原因分析:
SELECT * 会查询出所有字段,包括大文本字段,增加了网络传输和内存开销。更重要的是,它无法利用覆盖索引 (Covering Index) ,必须回表查询。
✅ 优化方案:
只查需要的字段。
SELECT phone FROM user WHERE name = '张三';
如果 idx_name_phone 存在,这句 SQL 可以直接从索引树拿到数据,无需回表,速度飞快!
案例七:like 模糊查询 % 在前 🔍
场景:根据名字模糊查询。
❌ 慢 SQL:
SELECT * FROM user WHERE name LIKE '%三';
💥 原因分析:
B+ 树索引是按照从左到右的顺序排列的。如果 % 在最前面,数据库不知道从哪里开始找,只能全表扫描。
✅ 优化方案:
- % 在后:LIKE '张%' 是走索引的。
- 覆盖索引:如果只查 ID,SELECT id FROM user WHERE name LIKE '%三' 是可以走索引扫描的。
- 搜索引擎:如果必须全模糊匹配,建议走 Elasticsearch。
💡 架构师总结
SQL 优化不是玄学,而是有迹可循的科学。
- Explain 是神器:写完 SQL,养成习惯用 EXPLAIN 看看执行计划,有没有走索引 (type 是不是 ALL)。
- 索引不是越多越好:索引会降低写入速度,占用磁盘空间。要根据查询场景精准建索引。
- 代码层面的优化:有时候 SQL 很难优化了,可以考虑加缓存、分库分表或者业务降级。
希望这篇文章能帮你消灭慢 SQL,让你的系统如丝般顺滑!