你的网站首页加载要等好几秒?用户点个按钮,光标转圈转到怀疑人生?别急着升级服务器——八成是数据库在偷偷拖后腿。SQL 写得随意、索引加得盲目、分页逻辑还停留在十年前……这些小问题堆在一起,比硬件瓶颈更伤用户体验。

你的 SQL 慢在哪?3 个最容易被忽略的细节

SQL 慢,真不全是数据库的锅。很多时候,是我们自己写的语句,亲手给查询挖了坑。

LIKE '%xxx%' 这种写法看着方便,但一用就废掉索引。我帮一个本地生活平台查慢查询,发现“搜商家名称”这个接口总卡住,原因就是前端传参没做约束,后端直接拼进 WHERE name LIKE ?,结果每次都是全表扫。

SELECT * 也常被当成省事写法。其实你只要用户 ID 和下单时间,却把地址、备注、图片 URL 全捞一遍。数据库得多读磁盘页、多传数据包、多占内存,最后响应时间自然拉长。

还有类型不匹配的问题。比如 user_idINT 类型,你却写了 WHERE user_id = '123'。MySQL 得先转类型再比对,索引直接失效。这种细节不注意,优化就白忙活。

索引不是越多越好,这 4 个原则才管用

索引不是装饰品,挂一堆反而拖累写入。我接手过一个 SaaS 后台系统,单张订单表有 17 个索引,每天定时同步数据时,插入一条记录都要等两秒——删掉 9 个长期不用的索引后,写入速度明显提升。

第一,只给真正高频查询的字段建索引。比如登录页查邮箱,WHERE email = ? 就必须有索引;但“用户籍贯”这种一年查不了几次的字段,别凑数。

第二,复合索引顺序很重要。如果你经常同时按城市+年龄筛选,那就建 (city, age),别反过来。MySQL 只认最左前缀,age = ? 单独查是用不上这个索引的。

第三,低区分度字段别硬加索引。性别、状态(0/1)、是否删除这类只有两三个值的字段,建了索引也大概率被优化器跳过。

第四,定期清理。用 SHOW INDEX FROM orders; 看看哪些索引的 Cardinality 是 0 或极低,再结合慢日志确认它们压根没被用过——删掉,写入压力立马轻一截。

怎么用查询缓存偷懒?一个配置就能省下大量计算

如果你的业务以读为主(比如资讯站、文档库、商品详情页),那 MySQL 自带的查询缓存,真的能立竿见影。

打开 my.cnf,找到 query_cache_type,设为 1,再调高 query_cache_size(比如 64M)。重启后,相同结构的 SELECT 语句,第二次执行基本不走磁盘。

之前帮一个技术博客调优,首页文章列表每秒被刷几十次,启用缓存后,平均响应时间缩短了不少。不过得提醒一句:如果表更新频繁(比如评论实时刷新、订单状态不停变),缓存反而会成为负担——每次写操作都可能清空大片缓存,导致命中率暴跌。这种场景,不如关掉全局缓存,只对明确静态的内容手动缓存到应用层。

子查询有多坑?用 JOIN 替代后速度翻倍

子查询写起来顺手,但执行计划往往很不友好。尤其 IN (SELECT ...)NOT IN (SELECT ...),容易触发临时表和全表扫描。

举个真实例子:一个电商后台要导出“近 30 天下单但未付款”的用户,原 SQL 是 WHERE user_id NOT IN (SELECT user_id FROM payments WHERE status=1)。数据量一过十万,跑一次要 4 秒多。改成 LEFT JOIN payments ON ... WHERE payments.id IS NULL 后,0.4 秒搞定。

另一个经验:EXISTSIN 更适合大数据量关联,但前提是子查询里能走索引。如果不确定,优先写 JOIN,逻辑清晰,执行计划也更容易预测。

分页查询慢到崩溃?改个参数就能解决

LIMIT 100000, 20 看似无害,实则让数据库先定位 10 万行,再取 20 条——前面那 10 万行全白扫了。

我们优化过一个社区问答站,热门话题下翻到第 50 页就卡住,用户流失率很高。后来改用游标分页:前端记住上一页最后一条记录的 created_at 时间戳,下一页请求变成 WHERE created_at < '2024-03-15 10:22:33' ORDER BY created_at DESC LIMIT 20。数据库只扫 20 行,响应快得像没查一样。

如果暂时没法改分页逻辑,至少给 ORDER BY 字段加上索引,并确保 WHERE 条件能命中索引。实在要偏移分页,可以用主键 ID 做中间层:先 SELECT id FROM posts WHERE status=1 ORDER BY created_at LIMIT 100000, 20,再 JOIN 原表查完整字段——比直接 LIMIT 扫描量小得多。

今天就能执行的 1 个操作步骤

打开你正在用的数据库管理工具(比如 phpMyAdmin、Navicat、DBeaver,或者直接连 MySQL 命令行),运行这行命令:

SHOW PROCESSLIST;

盯一眼 Time 列,找出执行时间超过 1 秒的 SQL。复制其中一条,开头加上 EXPLAIN 再执行一次,比如:

EXPLAIN SELECT * FROM orders WHERE user_id = 123 AND status = 1;

重点看两列:

  • typeALL?说明正在全表扫描,赶紧给 WHERE 条件里的字段加索引;
  • rows 显示几万甚至几十万?说明查询范围太大,要么加条件过滤,要么检查有没有隐式转换或函数包裹字段。

现在就挑一条最常出现的慢 SQL,动手改。改完立刻测,你会亲眼看到页面快了一截。