云数据库慢查询优化:从执行计划到索引设计
前言
业务上线后,数据库往往是最先成为瓶颈的一环。很多慢查询并不是因为"数据库不行",而是执行计划走了全表扫描、索引建得不对,或者写入时忘了预留缓冲。本文以 MySQL 为例,梳理一套可复用的慢查询排查思路,云上自建与云数据库实例同样适用。
一、先定位:慢日志是最诚实的信息源
排查慢查询的第一步永远是看日志、用数据说话,而不是凭感觉猜。开启慢查询日志后,重点关注三类 SQL:扫描行数远大于返回行数的、频繁出现的、以及响应时间 P99 明显偏大的。
拿到慢 SQL 后,用 EXPLAIN 查看执行计划。需要盯住的关键列只有几个:type(是否出现了 ALL 全表扫描)、key(实际用了哪个索引)、rows(预估扫描行数)、Extra(是否出现 Using filesort 或 Using temporary)。type 从 ALL 优化到 range 或 ref,通常就有数量级的提升。
二、索引设计的四个实战原则
1. 最左前缀原则要记牢。联合索引 (a, b, c) 能服务 a、a+b、a+b+c 三类查询,但单独查 b 或 c 就用不上。建索引前先列出高频查询的 WHERE 条件,把区分度高、出现频率高的列放在左边。
2. 覆盖索引是性价比之王。如果查询的列全在索引里,数据库可以直接从索引返回数据,不用回表。例如 SELECT a, b FROM t WHERE a = ?,索引 (a, b) 就比 (a) 快得多,回表开销在随机 I/O 场景下尤其明显。
3. 索引也会失效,注意写法细节。对索引列做函数运算(如 WHERE DATE(create_time) = ...)、隐式类型转换(如字符串列传数字)、前置通配符的 LIKE '%xxx',都会让索引失效。改写 SQL 往往比加索引更有效。
4. 写多读少的表要控制索引数量。每个二级索引都会在 INSERT、UPDATE、DELETE 时产生额外维护成本。一张索引十几个的表,写入延迟和磁盘占用都会明显上升。定期用性能 Schema 或慢日志反查"从未被使用"的索引并清理。
三、容易被忽略的三个坑
坑一:分页深翻页。LIMIT 1000000, 20 仍然要扫描并丢弃前 100 万行。改造成"记住上一页最后一条记录的主键,用 WHERE id > ? LIMIT 20"的方式,可以把深翻页的成本降到常量级。
坑二:大事务。一个事务里塞几千条 DML,不仅持有锁的时间变长,还会撑大 undo 日志,拖慢整个实例。拆成小批量提交(每批 500~1000 行),是线上最常见的急救手段。
坑三:连接数打满。慢查询堆积会导致连接数暴涨,进而拖垮实例。应用侧做好连接池上限、超时熔断,数据库侧关注 Threads_running 而不是 Threads_connected,前者才是真实的并发压力。
四、一份可直接套用的优化 checklist
1. 慢日志是否开启,是否定期归档分析;2. 核心表的执行计划是否定期 review;3. 是否存在全表扫描的高频 SQL;4. 索引是否遵循最左前缀、有无冗余;5. 深翻页、大事务、隐式转换三个坑是否已排查;6. 连接池配置与实例规格是否匹配。
数据库优化很少靠"黑科技",更多是把这些基本功做扎实。建议把上面这份 checklist 纳入上线前的评审项,比出问题再救火要划算得多。
- 点赞
- 收藏
- 关注作者
评论(0)