数据库

90% 的慢 SQL,都不是因为数据量大

转载:90% 的慢 SQL,都不是因为数据量大

前言🔖


很多人一看到 SQL 跑得慢,第一反应就是:数据量太大了。

这话有时候没错,但更多时候,它只是个看起来很合理的借口。

真实情况往往更扎心: 表不算大,机器也还行,索引甚至都建了,SQL 还是慢。
那问题多半不在“数据量”,而在 SQL 写法把数据库优化器整不会了

说得更直接一点: 慢 SQL,大多数不是“扛不住”,而是“写得烂”。

今天这篇,就聊几个最常见、最有代表性的坑。你会发现,很多性能问题,根本不是升级机器能解决的。

  

一、不是数据量大,而是你先让索引失效了🔖


数据库性能优化里,索引像高速公路。
你明明修了路,结果 SQL 一写,自己先把路封了,然后还怪车多。

最典型的几种写法:

🔹1. 对索引列做函数计算

比如:

SELECT *
FROM orders
WHERE DATE(create_time) = '2026-03-01';

如果 create_time 上有索引,很多人以为这条语句会走索引。
但现实通常是:不会。

因为你对列做了 DATE() 运算,数据库没法直接利用原始索引值,只能老老实实扫大量数据再计算。

更好的写法:

SELECT *
FROM orders
WHERE create_time >= '2026-03-01 00:00:00';
  AND create_time < '2026-03-02 00:00:00';

这类范围查询,才更容易真正利用索引。

  

🔹2. 隐式类型转换

比如手机号是字符串类型,你却拿数字去查:

SELECT *
FROM user_profile
WHERE phone = '13800138000';

看起来只是少打了两个引号,实际上数据库可能要做类型转换。
一旦转换方向不友好,索引利用率就会直接掉下去。

别小看这种低级错误。
线上很多“偶发性慢 SQL”,本质就是这种代码细节导致的。

  

🔹3. 前导模糊匹配

SELECT *
FROM product
WHERE product_name LIKE '%咖啡';

LIKE '%xxx' 这种前面带 % 的写法,B+ 树索引基本帮不上忙。
因为它无法从有序前缀开始定位,只能靠扫描。

如果你的业务里这种查询非常常见,那就别硬拿普通索引扛:

  • • 要么改业务检索方式
  • • 要么上倒排索引 / 全文检索
  • • 要么单独设计搜索服务

别拿数据库当搜索引擎,还怪它不够快。

  

二、不是数据多,而是你查了太多没用的数据🔖


很多 SQL 慢,问题甚至不在条件,而在 你拿太多了

🔹 1. SELECT * 是性能懒癌

SELECT *
FROM orders
WHERE user_id = 1001;

这句写起来最省事,后果通常最不省事。

为什么?

  • 读取了根本用不到的列
  • 增加了磁盘 IO
  • 增加了网络传输
  • 可能让覆盖索引失效

如果你真正只需要:

SELECT order_id, amount, status
FROM orders
WHERE user_id = 1001;

那就老老实实只查这三列。

很多场景下,字段一收窄,性能就肉眼可见地好起来。

  

🔹 2. 没有限制返回行数

有些接口明明前端只展示 20 条,你后端 SQL 却先捞 5 万条回来,再在代码里慢慢处理。

这不是数据库慢,这是你对数据库不太客气。

该加 LIMIT 的地方就加,
该做分页的地方就分页,
该做游标翻页的地方别硬上深分页。

  

三、不是数据量问题,而是 JOIN / 子查询写得太任性🔖


SQL 慢,不少时候慢在“关系太复杂”,不是“数据太大”。

🔹1. 小表驱动大表,不是玄学,是常识

如果多表关联时,过滤条件没有尽早生效,数据库可能会先生成很大的中间结果集,再一步步筛。

这就像你先把全公司的人都拉进会议室,再问一句“做后端的留下”。

不慢才怪。

优化思路通常包括:

  • 先过滤再关联
  • 给关联列加合适索引
  • 避免无谓的宽表 JOIN
  • 看执行计划,确认驱动表是否合理

  

🔹2. 子查询不是原罪,但乱写会出事

比如相关子查询:

SELECT u.id,
       (SELECT COUNT(*) FROM orders o WHERE o.user_id = u.id) AS order_cnt
FROM users u;

如果外层 users 记录很多,内层子查询可能被反复执行。

在一些场景下,改成聚合后再 JOIN,性能会稳定很多:

SELECT u.id, t.order_cnt
FROM users u
LEFT JOIN (
    SELECT user_id, COUNT(*) AS order_cnt
    FROM orders
    GROUP BY user_id
) t ON u.id = t.user_id;

当然,具体还要看数据库版本、优化器能力和实际执行计划。
但有一点不会变: 别只看 SQL 能不能跑,要看它到底怎么跑。

  

四、不是表太大,而是排序、分组没借上索引的力🔖


🔹1. ORDER BY 一旦排不动,就开始消耗内存和磁盘

SELECT id, user_id, create_time
FROM orders
WHERE status = 'paid';
ORDER BY create_time DESC;

如果 statuscreate_time 上没有形成合适的联合索引,数据库就可能先筛选,再做额外排序。

数据一多,排序成本非常高,甚至会产生临时文件。

很多人看到慢,就说“因为订单表太大”。
其实更准确的说法是: 你想排序,但没给数据库一条能顺着走的路。

  

🔹2. GROUP BY 也一样

分组统计本身就有成本。
如果还能顺着索引分组,成本会低很多;
如果不能,就容易出现临时表、额外排序、CPU 飙高。

所以别只盯着 where 条件建索引,
ORDER BYGROUP BY 的字段顺序,也常常决定了这条 SQL 的生死。

  

五、不是数据量大,而是分页方式错了🔖


很多后台系统一上来就是这种分页:

SELECT *
FROM orders
ORDER BY id
LIMIT 100000, 20;

这类深分页,页数越往后越慢。
原因不是数据库突然变笨了,而是它需要先跳过前面大量数据,再拿后面的 20 条。

这就像让人从一本 10 万页的书里,先翻过前 99980 页,再给你看最后 20 页。

更稳的方式通常是 基于游标或主键翻页

SELECT *
FROM orders
WHERE id > 100000
ORDER BY id
LIMIT 20;

如果业务允许,这种方式通常比 LIMIT offset, size 可靠得多。

  

六、不是数据库不行,而是你根本没看执行计划🔖


很多性能问题,讨论半天都停留在猜测层面:

  • 是不是数据太大?
  • 是不是数据库版本不行?
  • 是不是机器配置不够?
  • 是不是该分库分表了?

结果一看 EXPLAIN,发现压根没走索引。

这就像病人咳嗽两声,你直接提议换器官。

先看这几个关键信号:

  • type:是不是全表扫描
  • key:实际用了哪个索引
  • rows:预估扫描了多少行
  • Extra:有没有 Using filesortUsing temporary

很多所谓的大问题,执行计划一拉出来,十分钟就能定位。

别上来就谈架构升级。
先看看是不是一条 SQL 写得不太像样。

  

七、什么时候“数据量大”才真的是核心问题?🔖


说了这么多,不是要否认数据量的影响。

数据量当然会带来性能压力,尤其在这些场景里:

  • 热数据非常集中
  • 单表达到千万、上亿级别
  • 统计分析型查询很多
  • 索引体积巨大,缓存命中下降
  • 写入频繁,索引维护成本高

但重点是: 很多团队还没到那个量级,就已经先把 SQL 写出了亿级数据的灾难感。

也就是说,
本来是“自行车通勤”,
非要把自己骑出“横穿无人区”的痛苦体验。

  

八、排查慢 SQL,建议按这 5 步走🔖


如果你想系统一点排查,建议按这个顺序:

🔹1. 先看执行计划

不要猜,先确认有没有走索引、扫描了多少行、有没有临时表和额外排序。

  

🔹2. 看查询条件是否让索引失效

重点查:

  • 函数操作
  • 隐式转换
  • 前导模糊匹配
  • 不合理的联合索引顺序

  

🔹3. 看是不是查了太多字段、太多行

能不能去掉 SELECT *
能不能缩小结果集?
能不能提前过滤?

  

🔹4. 看 JOIN、排序、分组是否设计合理

很多 SQL 慢,不在 where,而在 join / order by / group by。

  

🔹5. 最后再考虑数据量和架构问题

如果 SQL 写法已经比较健康,索引也合理,执行计划也没明显问题,这时候再考虑分库分表、冷热分离、缓存、搜索引擎替代等方案。

顺序别反了。
一上来就上大手术,通常只是把一个小毛病包装成大项目。

  

结语🔖


慢 SQL 这件事,最误导人的一句话就是: “因为数据量大。”

它听起来专业,甩锅也方便,甚至还能顺便为后续买机器、上中间件、做复杂架构铺垫。

但很多时候,真相没那么宏大。

真相往往是:

  • 索引失效了
  • 查多了
  • 排序方式不对
  • 分页写法不对
  • 执行计划压根没看

所以,下次再遇到慢 SQL,先别急着怪数据量。

先问一句更实际的话: 这条 SQL,到底是不是“像个懂事的 SQL”?

这个问题,通常比“表有多大”更接近真相。