先搞懂:为什么你的SQL查询变慢了?
想优化查询,得先找到“慢”的根源。常见的慢查询原因就三个:全表扫描(没用到索引,遍历整个表找数据)、回表太多(用到了索引,但索引没包含查询字段,得再查主键索引拿数据)、索引失效(建了索引但没用到)。

怎么确认?用EXPLAIN
看执行计划!比如你写了SELECT * FROM user WHERE age > 30;
,跑一下EXPLAIN
:
– 如果type
是ALL
,说明全表扫描,没用到索引;
– 如果key
是NULL
,肯定没走索引;
– 如果rows
数值很大(比如10万+),说明扫描了太多数据。
举个真实例子:某电商系统的用户表有100万条数据,没建age
索引时,查age>30
的用户要2.3秒;建了索引后,只需要0.01秒——差距是230倍!
索引设计的3个黄金原则:不瞎建,建对才有用
索引不是越多越好(多了会拖慢插入/更新速度),得遵循“有用、高效、不冗余”的原则。
原则1:选择性高的字段优先建索引
选择性是指“字段不同值的数量/总记录数”——比如身份证号的选择性是1(每个值唯一),性别字段的选择性是0.5(只有男/女)。选择性越高,索引区分度越好,查得越快。
例子:给user
表的id_card
(身份证号)建索引有用,给gender
(性别)建索引没用——因为查“性别=男”会扫50万条数据,和全表扫描差不多。
原则2:联合索引遵循“最左匹配”
联合索引是多个字段的组合(比如(age, name)
),查询时必须从左到右匹配字段,否则索引失效。
正确用法:
– 查age>30
(用左1字段);
– 查age=30 AND name LIKE '张%'
(用左1+左2字段);
错误用法:
– 查name LIKE '张%'
(跳过左1字段,索引失效)。
原则3:覆盖索引优先,减少回表
覆盖索引是指“查询的所有字段都在索引里”——比如查SELECT id, name FROM user WHERE age>30
,如果建了(age, name, id)
的联合索引,不用再回表查主键索引,直接从索引拿数据,速度翻倍。
对比:
– 没覆盖索引:查age>30
→用age
索引→回表查id
和name
→2次扫描;
– 有覆盖索引:查age>30
→直接从(age, name, id)
索引拿数据→1次扫描。
查询优化的4个实战技巧:写SQL时就避开慢坑
技巧1:拒绝SELECT *
,只查需要的字段
SELECT *
会把所有字段查出来,要么全表扫描,要么回表多次。改成查具体字段,刚好凑成覆盖索引,速度快很多。
例子:
– 慢:SELECT * FROM order WHERE create_time > '2025-01-01'
(要回表查10个字段);
– 快:SELECT order_id, user_id FROM order WHERE create_time > '2025-01-01'
(建(create_time, order_id, user_id)
索引,直接覆盖)。
技巧2:用JOIN
代替子查询
子查询会生成临时表,JOIN
是直接关联表,效率更高。
例子:
– 慢:SELECT * FROM order WHERE user_id IN (SELECT id FROM user WHERE age>30)
(子查询生成临时表);
– 快:SELECT o.* FROM order o JOIN user u ON o.user_id = u.id WHERE u.age>30
(直接关联,用到user
表的age
索引)。
技巧3:别在索引字段上做运算
给索引字段用函数(比如DATE(create_time)
)、加减乘除(比如age+1=31
),会让索引失效——数据库无法直接用索引查,只能全表扫描。
错误→正确:
– 错:WHERE DATE(create_time) = '2025-08-24'
(函数操作索引字段);
– 对:WHERE create_time BETWEEN '2025-08-24 00:00:00' AND '2025-08-24 23:59:59'
(用范围查询,用到create_time
索引)。
技巧4:批量操作代替循环单条
插入/更新1000条数据,用INSERT INTO ... VALUES (a1,b1),(a2,b2)...
比循环INSERT
快10倍——因为减少了数据库连接次数和日志写入次数。
例子:
– 慢:循环1000次INSERT INTO user(name) VALUES ('张三')
;
– 快:INSERT INTO user(name) VALUES ('张三'),('李四'),...
(1次执行,1000条数据)。
避坑:90%的人会踩的索引失效雷区
雷区1:模糊查询以%
开头
比如WHERE name LIKE '%张三'
——索引是按“张→张三→张三丰”排序的,%
开头意味着“不管前面是什么”,数据库无法用索引,只能全表扫描。改成LIKE '张三%'
(固定前缀),就能用到索引。
雷区2:隐式类型转换
比如user
表的id
是INT
类型,你写WHERE id='123'
(字符串),数据库会隐式转成INT
,但可能导致索引失效。直接写WHERE id=123
(数字),稳用索引。
雷区3:用OR
连接未索引的字段
比如WHERE age>30 OR gender='男'
——如果gender
没建索引,数据库会放弃age
索引,直接全表扫描。要么给gender
建索引,要么拆成两个查询用UNION
。
雷区4:联合索引跳过左字段
比如建了(a,b,c)
的联合索引,查WHERE b=2 AND c=3
——跳过左1字段a
,索引失效。必须包含左1字段(比如WHERE a=1 AND b=2
)。
雷区5:索引字段用函数
比如WHERE UPPER(name) = 'ZHANGSAN'
(把name
转大写)——函数会破坏索引的排序,导致索引失效。改成WHERE name = 'zhangsan'
(存数据时转小写,查的时候用小写)。
用EXPLAIN
验证:你的优化有没有用?
优化后要不要看效果?用EXPLAIN
跑一遍执行计划,看3个关键字段:
– type:从差到好是ALL
(全表)→range
(范围)→ref
(索引查找)→eq_ref
(唯一索引);
– key:显示用到的索引(NULL
就是没用到);
– rows:扫描的行数(越少越好)。
例子对比:
– 优化前:EXPLAIN SELECT * FROM user WHERE age>30
→type=ALL
(全表)、rows=100000
(扫10万条);
– 优化后:EXPLAIN SELECT id,name FROM user WHERE age>30
→type=range
(范围)、key=idx_user_age_name
(用了联合索引)、rows=1000
(扫1000条)。
附:常用索引类型对比表
索引类型 | 特点 | 使用场景 |
---|---|---|
主键索引 | 唯一、非空、默认创建 | 表的主键(比如id ) |
唯一索引 | 唯一、可空 | 唯一字段(比如phone 手机号) |
普通索引 | 无约束 | 常用查询字段(比如age ) |
联合索引 | 多字段组合 | 多条件查询(比如age+name ) |
覆盖索引 | 查询字段都在索引里 | 避免回表(比如age+name+id ) |
最后:优化是“迭代”,不是“一步到位”
没有完美的索引,只有适合业务的索引。比如初期用户少,age
索引够用;用户到100万时,得加联合索引;业务变了,要定期删除无用索引(比如gender
索引)。
小建议:每周跑一次slow_query_log
(慢查询日志),找出top10的慢查询,用上面的方法优化——坚持1个月,数据库性能至少提升50%!
原创文章,作者:,如若转载,请注明出处:https://zube.cn/archives/203