跳过导航,直达内容
2026-03-20 技术团队 10 分钟

数据库性能优化实战:从慢查询到毫秒级响应的调优之路

数据库性能优化后端
数据库性能优化实战:从慢查询到毫秒级响应的调优之路

我们排查过的性能问题里,最终落在数据库上的占大多数——不是数据库不够快,而是索引、SQL 写法和连接配置出了问题。这篇按"先看什么、再改什么"的顺序整理,末尾附一份可以直接用的自检清单。

先量再改:慢查询日志 + EXPLAIN

优化最忌讳凭感觉加索引。我们的固定动作是两步:先开慢查询日志(slow_query_log = ON、long_query_time = 1),让它跑一天,看哪些 SQL 真的慢、慢多少次;再用 EXPLAIN 看执行计划,确认慢在哪一步。

🔍 EXPLAIN 看三个字段就够:type 列避免 ALL(全表扫描)与 index(全索引扫描),目标是 ref / eq_ref / range;rows 是预估扫描行数,越小越好;Extra 里出现 Using filesort 或 Using temporary,说明排序或分组没吃到索引。

三种最常见的"索引失效"

很多慢查询并不是没索引,而是索引没用上。这三类我们在项目里反复遇到:

  1. 隐式类型转换。字段是 varchar,查询写成 WHERE order_no = 12345(传了数字),索引直接失效。参数化查询时注意类型对齐。
  2. 在索引列上做运算或函数。WHERE DATE(created_at) = '2026-01-01' 用不上索引,改成范围条件 created_at >= '2026-01-01' AND created_at < '2026-01-02'。
  3. 联合索引不满足最左前缀。(a, b, c) 能覆盖 WHERE a=1 与 WHERE a=1 AND b=2,但覆盖不了 WHERE b=2。排序与分组也要和索引列顺序一致,否则照样 filesort。

顺带一句:索引不是越多越好。每个索引都要在写入时维护,还会占空间;我们更倾向"先删掉没被用到的冗余索引,再补真正需要的"。

一个真实的优化过程

某订单列表查询耗时 5.8 秒。EXPLAIN 显示:全表扫描约 200 万行 + 文件排序 + 临时表。三步处理:

  1. 建立 (user_id, created_at) 联合索引 → type 变为 range,预估行数降到几百。
  2. 分页只取需要的一页(LIMIT 20),避免一次把全部数据捞回应用层。
  3. 把查询字段收敛成覆盖索引能提供的列 → Extra 变成 Using index,去掉回表。

优化后 5.8s → 80ms。真正起作用的其实是前两步,覆盖索引是锦上添花。

分页与连接池:两个容易被忽略的地方

深度分页。LIMIT 100000, 20 会让数据库扫描并丢弃前十万行——页数越深越慢。业务上能接受时改用游标分页(带上上一页最后一条的 id 或时间),WHERE id > ? ORDER BY id LIMIT 20,成本恒定。

连接池。池子太小会在高并发时排队,太大反而拖垮数据库(大量线程上下文切换)。我们的做法是从一个保守值开始(例如 CPU 核数 × 2 + 磁盘数 作为起点),再按压测调整,并确认应用侧用完即还、没有泄漏。

📋 数据库自检清单

  • □ 慢查询日志开着吗?阈值是多少?(建议先设 1 秒)
  • □ 高频 SQL 是否都用 EXPLAIN 看过?type 里有没有 ALL?
  • □ 有没有在索引列上做函数运算或类型不一致的比较?
  • □ 是否存在深度分页?能否改成游标分页?
  • □ 有没有长期没被命中的冗余索引?
  • □ 连接池大小是否压测过?应用侧有没有连接泄漏?
  • □ 大表有没有归档或分区策略(历史数据不必和热数据挤在一起)?

比调优更重要的是可观测

真正让这类问题不再"突然爆发"的,是把监控补齐:慢查询趋势、连接池使用率、关键 SQL 的 P95 耗时。有了这些,性能问题会在变成故障前先被看到。我们在项目里默认把这几项纳入上线检查,如果你手上有系统正在"高峰期特别慢",欢迎联系我们,先把慢查询日志跑起来,往往第一天就能定位到主因。