dbaplus社群

还找借口?!有90%的慢SQL,都不是因为数据量大

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

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

真实情况往往更扎心: 表不算大,机器也还行,索引甚至都建了,SQL 还是慢。

那问题多半不在“数据量”,而在 SQL 写法把数据库优化器整不会了。

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

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

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

数据库性能优化里,索引像高速公路。

你明明修了路,结果 SQL 一写,自己先把路封了,然后还怪车多。

最典型的几种写法:

1、对索引列做函数计算

比如:

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

如果 create_time 上有索引,很多人以为这条语句会走索引。

但现实通常是:不会。

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

更好的写法:

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

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

2、隐式类型转换

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

SELECT *FROM user_profileWHERE phone = '13800138000';

看起来只是少打了两个引号,实际上数据库可能要做类型转换。

一旦转换方向不友好,索引利用率就会直接掉下去。

别小看这种低级错误。

线上很多“偶发性慢 SQL”,本质就是这种代码细节导致的。

3、前导模糊匹配
SELECT *FROM productWHERE product_name LIKE '%咖啡';

LIKE '%xxx' 这种前面带 % 的写法,B+ 树索引基本帮不上忙。

因为它无法从有序前缀开始定位,只能靠扫描。

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

  • 要么改业务检索方式

  • 要么上倒排索引 / 全文检索

  • 要么单独设计搜索服务

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

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

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

1、SELECT * 是性能懒癌
SELECT *FROM ordersWHERE user_id = 1001;

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

为什么?

  • 读取了根本用不到的列

  • 增加了磁盘 IO

  • 增加了网络传输

  • 可能让覆盖索引失效

如果你真正只需要:

SELECT order_id, amount, statusFROM ordersWHERE 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_cntFROM users u;

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

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

SELECT u.id, t.order_cntFROM users uLEFT 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_timeFROM ordersWHERE status = 'paid';ORDER BY create_time DESC;

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

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

很多人看到慢,就说“因为订单表太大”。

其实更准确的说法是: 你想排序,但没给数据库一条能顺着走的路。

2、GROUP BY 也一样

分组统计本身就有成本。

如果还能顺着索引分组,成本会低很多;

如果不能,就容易出现临时表、额外排序、CPU 飙高。

所以别只盯着 where 条件建索引,

ORDER BY、GROUP BY 的字段顺序,也常常决定了这条 SQL 的生死。

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

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

SELECT *FROM ordersORDER BY idLIMIT 100000, 20;

这类深分页,页数越往后越慢。

原因不是数据库突然变笨了,而是它需要先跳过前面大量数据,再拿后面的 20 条。

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

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

SELECT *FROM ordersWHERE id > 100000ORDER BY idLIMIT 20;

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

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

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

  • 是不是数据太大?

  • 是不是数据库版本不行?

  • 是不是机器配置不够?

  • 是不是该分库分表了?

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

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

先看这几个关键信号:

  • type:是不是全表扫描

  • key:实际用了哪个索引

  • rows:预估扫描了多少行

  • Extra:有没有 Using filesort、Using temporary

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

别上来就谈架构升级。

先看看是不是一条 SQL 写得不太像样。

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

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

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

  • 热数据非常集中

  • 单表达到千万、上亿级别

  • 统计分析型查询很多

  • 索引体积巨大,缓存命中下降

  • 写入频繁,索引维护成本高

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

也就是说,本来是“自行车通勤”,

非要把自己骑出“横穿无人区”的痛苦体验。

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

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

1、先看执行计划

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

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

重点查:

  • 函数操作

  • 隐式转换

  • 前导模糊匹配

  • 不合理的联合索引顺序

3、看是不是查了太多字段、太多行
  • 能不能去掉 SELECT *?

  • 能不能缩小结果集?

  • 能不能提前过滤?

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

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

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

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

顺序别反了。

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

结语

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

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

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

真相往往是:

  • 索引失效了

  • 查多了

  • 排序方式不对

  • 分页写法不对

  • 执行计划压根没看

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

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

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

作者丨胡萝卜数据
来源丨公众号:胡萝卜数分(ID:gh_7d85a2defab9)
dbaplus社群欢迎广大技术人员投稿,投稿邮箱:[email protected]
Image
Image