慢查询是怎么被发现的

"系统慢"是个模糊的感受,治理的第一步是把它变成可量化的慢查询清单。数据库默认会把超过阈值的 SQL 记到慢查询日志里,把阈值调到比如 200 毫秒,跑一段时间后就能拿到一份"谁最慢"的列表。再配合监控工具统计 QPS、连接数、锁等待、缓存命中率,先搞清楚是哪几条 SQL 在拖后腿,而不是凭感觉优化整个库。

经验是:百分之八十的慢,来自百分之二十的几条 SQL。把最慢的头几条抓出来逐个击破,收益远大于全局调参。

本文核心知识点
  • 慢查询日志先把慢 SQL 量化成可优化的清单。
  • EXPLAIN看执行计划,判断是否走索引、扫了多少行。
  • 索引优化建对索引比加机器更有效。
  • 治理流程发现→分析→优化→验证,闭环推进。
数据库性能优化配图:蓝色渐变背景中央仪表盘式数据库圆柱,指针指向绿色区域,周围曲线图、放大镜与慢查询沙漏
仪表盘指向绿区代表性能达标,放大镜与沙漏代表慢查询排查——这就是性能治理的日常画面。

读懂执行计划 EXPLAIN

拿到慢 SQL 后,下一步是 EXPLAIN 看执行计划。重点看三个字段:type 表示访问类型,从全表扫描(ALL)到索引范围(range)再到唯一等值(const),越靠前越好;key 表示实际用到的索引,是 null 就说明没走索引;rows 是预估扫描行数,越大说明效率越低。

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

-- 关注输出里的:
-- type: ref(走了索引)还是 ALL(全表扫描)
-- key:  实际命中的索引名
-- rows: 预估扫描行数

看到 type=ALL 且 rows 很大,基本就是缺索引;走了索引但 rows 仍大,可能是索引区分度太低(比如对 status 这种只有几个值的列建索引)。

索引怎么建才不白建

索引不是建得越多越好。每个索引都会让写入变慢、占存储空间,建错的索引等于白占。几条原则:为高频查询的 where、join、order by 字段建联合索引,遵循最左前缀;区分度低的列(性别、状态)单独建索引意义不大,要和高频查询列组合;避免冗余索引(已有 (a,b) 就不必再单列 (a))。

定期用工具统计哪些索引从未被使用,把它们删掉——没用的索引只会拖慢写入。

SQL 写法的常见性能陷阱

很多慢不是缺索引,而是写法让索引用不上。常见陷阱:对索引列做函数或运算(where date(create_time) = '2026-10-01' 会让索引失效,要改成范围查询);like 以通配符开头('%关键词' 无法走索引);隐式类型转换(字符串列用数字查);select * 拉回不需要的列,尤其回表时多读很多数据。

-- 慢:对索引列做函数,索引失效
SELECT * FROM orders WHERE DATE(create_time) = '2026-10-01';

-- 快:改成范围查询,能走 create_time 索引
SELECT * FROM orders
WHERE create_time >= '2026-10-01' AND create_time < '2026-10-02';

表结构与参数调优

除了索引和 SQL,表结构也影响性能:列类型尽量小(能用 int 别用 bigint、能用 varchar 短长度别留长)、避免过度宽表、冷热数据分表存放。参数调优上,innodb_buffer_pool_size 设为物理内存的 50%–70% 让数据尽量在内存里、连接数按实际并发设、慢查询日志阈值打开。参数是最后一步,先把索引和 SQL 优化到位,再谈调参。

读写分离与分库分表的边界

单表优化到顶还不够,就要靠架构层。读写分离把读请求分摊到从库,适合读多写少;分库分表把单表数据拆散,适合单表过大。但两者都有代价:读写分离有主从延迟,读到的可能不是最新数据;分库分表引入跨片查询与分布式事务。先确认单表优化、缓存、读写分离都到顶了,再上分库分表。