慢查询是怎么被发现的
"系统慢"是个模糊的感受,治理的第一步是把它变成可量化的慢查询清单。数据库默认会把超过阈值的 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 优化到位,再谈调参。
读写分离与分库分表的边界
单表优化到顶还不够,就要靠架构层。读写分离把读请求分摊到从库,适合读多写少;分库分表把单表数据拆散,适合单表过大。但两者都有代价:读写分离有主从延迟,读到的可能不是最新数据;分库分表引入跨片查询与分布式事务。先确认单表优化、缓存、读写分离都到顶了,再上分库分表。
治理流程与监控
性能治理不是一次性优化,而是持续闭环:慢查询日志持续采集、定期复盘 TOP 慢 SQL、每次发版前对核心 SQL 做执行计划检查。监控上盯住几个关键指标:QPS、慢查询数量、缓存命中率、连接数、锁等待时间。这些指标异常时及时告警,性能问题在影响用户之前就被发现。
治理清单
把治理流程浓缩成清单:
- 开慢查询日志,先量化出 TOP 慢 SQL。
- 逐条 EXPLAIN,看 type、key、rows。
- 缺索引就补联合索引,遵循最左前缀。
- 修写法:不对索引列做函数、避免前置通配符。
- 删冗余与未使用索引,减少写入负担。
- 单表优化到顶再上读写分离与分库分表。
- 监控持续盯指标,形成治理闭环。
数据库性能优化是"先测量、再动手、持续验证"的工程,不是玄学。把慢 SQL 一条条抓出来、用执行计划定位原因、针对性改索引或写法,大多数"系统慢"都会在这个闭环里被逐个消解。