MySQL慢查询拖垮业务,调优先抓这几个参数
业务刚上线时数据库跑得挺顺,等订单表过了几百万行,页面加载开始变慢,监控里 CPU 和磁盘 IO 一起往上顶。这时候最容易被带偏的方向是直接加配置:内存翻倍、SSD 换 NVMe。钱花出去了,慢查询该慢还是慢。调优这件事,顺序比力度重要——先定位瓶颈在哪一层,再决定是改参数、改 SQL 还是换机器。
先让慢查询自己说话
MySQL 默认不记录慢查询。没有日志,所有优化都是猜。第一步把 slow_query_log 打开,long_query_time 设成 1 秒,先跑一两天看真实分布。
拿到日志别急着逐条改。用 mysqldumpslow 或 pt-query-digest 按总耗时排序,排前面的几条往往占了八成时间。我一般先看三样东西:扫描行数和返回行数的比例、有没有 Using filesort、有没有 Using temporary。扫描 100 万行只返回 10 行,说明索引没走对。
这里有个常见误区。慢查询日志里耗时最长的那条,不一定是最该改的。它可能一天只跑一次,属于报表任务。真正拖垮在线业务的是那种单次 50ms、每秒跑几百次的查询,日志里排名未必靠前,但累计占用最多。
缓冲池大小决定一半的性能
InnoDB 把数据和索引缓存在内存里,这个池子就是 innodb_buffer_pool_size。池子装得下热数据,读就走内存;装不下,每次查询都要落盘。两者延迟差着数量级。
在只有 MySQL 的独立服务器上,通常可以给到物理内存的 50% 到 70%。一台 32G 内存的机器,缓冲池给 20G 左右比较稳。但要注意,别把内存全给缓冲池,操作系统、连接线程、排序缓冲都要留余量。缓冲池命中率低于 99%,基本可以判断内存不够用。
调整缓冲池大小在 MySQL 8.0 支持在线改,不用重启。但改完要观察一段时间,因为预热需要时间,刚改完命中率反而会掉。
如果算下来热数据体积远超单机内存上限,加内存的性价比就开始下降。这时候可以考虑把实例迁到内存更大的物理服务器上,马来西亚服务器2 这类 E5-2683v4*2 配 64G 内存的配置,缓冲池能给到 40G 上下,对几千万行级别的库比较从容。选机器时内存比 CPU 更值得优先堆,MySQL 多数场景不是算力瓶颈。
索引要建在查询真正用到的地方
EXPLAIN 是绕不开的工具。看 type 列,出现 ALL 就是全表扫描,出现 index 是全索引扫描,这两种都要警惕。理想状态是 ref 或 range。
联合索引的顺序很讲究。WHERE a=1 AND b=2 这种查询,索引建 (a,b) 和 (b,a) 效果完全不同,取决于字段的选择性。选择性高的字段放前面,能更快把范围缩小。范围查询的字段要放最后,因为它后面的字段用不上索引。
- 避免在索引列上做函数运算,WHERE DATE(created_at)='2026-01-01' 会让索引失效,改成范围比较
- 覆盖索引能省掉回表,SELECT 的字段尽量都在索引里
- 索引不是越多越好,每个索引都占写入开销,写多读少的表要克制
改 SQL 有时候比加机器划算得多。一条全表扫描的查询改成走索引,效果立竿见影。但也要分清哪些 SQL 能改、哪些不能。业务逻辑写死的复杂查询,改造成本可能高于直接扩容。这个取舍得算清楚。
参数之外,别忽略连接和锁
数据库变慢的原因不全是查询本身。连接数打满、锁等待堆积,表现出来也是响应变慢,但方向完全错了。
max_connections 设得太小,新连接直接被拒;设得太大,每个连接都占内存,反而拖垮实例。通常根据业务并发量估算,留一定余量就行。连接池层也要配合,应用侧连接池上限不能超过数据库 max_connections,否则一样排队。
锁的问题更难查。用 SHOW ENGINE INNODB STATUS 看最近一次死锁,或者查 information_schema 里的锁等待。长事务是锁等待的常客,一个没提交的事务持有行锁,后面全堵着。把事务范围缩小、及时提交,往往比调参数更有效。
按这个顺序动手
总结一下我常用的排查路径:先开慢日志定位高频慢查询,再看缓冲池命中率判断是不是内存问题,然后 EXPLAIN 检查索引,最后才看连接和锁。硬件升级放在最后,因为前面几步不花钱就能见效。
具体怎么选:几百万行以内的小库,8G 到 16G 内存的机器够用,重点在 SQL 和索引。数据量上到千万行,内存要给到 32G 以上,缓冲池才有意义。如果单表过亿且还在增长,单机调优的天花板就到了,该考虑分库分表,那已经不是参数能解决的问题。