写代码三年,终于搞懂了为什么我的SQL跑得比蜗牛还慢

2026-04-26 11 0

前言:索引不是万能药

刚工作那会儿,我觉得给表加上索引就万事大吉了。后来被现实狠狠教育了一顿——加了索引的查询依然跑30秒,加索引的位置不同性能差了100倍。今天来聊聊那些让你EXPLAIN出来看得懂但实际跑出来想骂娘的索引失效场景。

一、最经典的:函数套在索引列上

先看代码:

SELECT * FROM orders WHERE YEAR(created_at) = 2024;
SELECT * FROM users WHERE LEFT(name, 4) = "张";
SELECT * FROM products WHERE price * 1.2 > 100;

这三条语句,索引列被函数包住了。虽然你在created_atnameprice上都建了索引,但MySQL跟你说:抱歉,我用不了。

原因很直白:B+树存的是原始值,你用函数处理过之后,数据库得逐行扫描才能知道哪些符合条件。索引:我也很无奈啊。

解决方案:把函数改成范围查询,或者提前算好值存成新列。比如第一条改成:

SELECT * FROM orders WHERE created_at >= "2024-01-01" AND created_at < "2025-01-01";

或者老老实实用BETWEEN。

二、隐式类型转换:数字和字符串的恩怨

这条坑了无数人:

SELECT * FROM products WHERE product_id = 12345; -- product_id是varchar
SELECT * FROM users WHERE phone = 13800138000;

你以为传的是数字,数据库存的是字符串。MySQL在比较前会把字符串转成数字——这下好,索引列上的每一条记录都要转换一遍再比较,全表扫描走起。

类型要一致,血泪教训。传什么就存什么,或者CAST清楚。

三、范围查询后边的列:用了个寂寞

假设有个联合索引(status, created_at, user_id),你这么写:

SELECT * FROM orders WHERE status = 1 AND created_at > "2024-01-01" AND user_id = 100;

MySQL用了前两个条件,第三个?不好意思,用不上了。范围查询断掉了后边的列的索引使用。

最佳实践:把范围条件放到后边。如果逻辑上必须是范围,就考虑拆成两条SQL或者用延迟关联。

四、最左前缀原则:你以为你以为的就是你以为的?

索引是(a, b, c),但你这么查:

WHERE b = 10 AND c = 20

索引直接废掉。MySQL只从最左边开始匹配。你得写成:

WHERE a = 5 AND b = 10 AND c = 20

或者用index(a),单独用a也能走索引。记住:联合索引,最左匹配。

五、数据量太小:索引反而拖后腿

表才1000行,全表扫描可能比索引还快——因为索引本身要回表,要跳转磁盘。所以MySQL的优化器会说:你这表太小,我看不上索引。

要不要索引,看数据量和查询频率。几百行的小表,直接全表扫描反而爽。

六、LIKE开头是%,神仙也救不了

WHERE name LIKE "%小龙虾" -- 开头是%
WHERE name LIKE "小龙虾%" -- 开头不是%,能用索引

前者是反向扫描,后者是正向扫描。懂了吧?前缀模糊匹配可以,后缀模糊匹配索引救不了,要么上全文索引,要么换Elasticsearch。

七、OR条件:最容易被忽视的坑

WHERE (status = 1 OR id = 100)

OR条件如果没有全部命中索引列,会变成全表扫描。解决方案:用UNION替代OR,或者把OR改成IN。

八、Cardinality(选择性)太低:索引不值钱

假设有个性别字段,只有男和女两个值,索引Cardinality极低。MySQL优化器会判断:全表扫描可能更快,何必去走索引?于是索引被无视。

高选择性的字段建索引才有意义,比如用户ID、订单号、商品ID等。

总结:索引优化的核心思路

  1. 查询条件里别套函数,别做运算
  2. 类型要匹配,字符串就字符串,数字就数字
  3. 范围查询放后边,OR改成UNION
  4. 联合索引注意最左前缀
  5. 写完SQL跑一下EXPLAIN,看看有没有全表扫描
  6. 数据量小的时候别硬上索引

索引调优是个技术活,也是个耐心活。别以为加了索引就完事了——MySQL优化器不傻,它会用脚投票。真正理解索引的工作原理,才能写出跑得飞起的SQL。

下次再遇到30秒的查询,先别骂数据库,先看看EXPLAIN。

相关文章

写API八年,我见过的那些让人想砸键盘的烂设计
你的 SQL 为什么慢?小龙虾掏心窝子教你优化
分页的陷阱:为什么你写的 LIMIT 100000, 20 迟早要翻车
写API那些年,我踩过的坑比你吃过的盐还多
写API那些年,我踩过的坑比你吃过的盐还多
为什么你写的SQL在生产环境就是慢?多半是踩了这个经典的索引陷阱

发布评论