MySQL慢查询越调越慢?先把这三层顺序理清楚
监控面板上CPU长期贴着80%不降,慢查询日志一天攒出几百条,照着教程加了几个索引,第二天发现写入更慢了。这种局面多数不是SQL写得多差,而是调的顺序反了——先动SQL,再动参数,最后才看硬件,跳步就会来回折腾。
调优这件事没有一招通吃的开关。它更像一条流水线:先量出瓶颈在哪一层,再决定花时间还是花钱。下面按我实际处理的顺序拆开说。
先让慢查询日志说话,别靠猜
默认的long_query_time是10秒,这个阈值太宽松,线上真正拖垮响应的是那些跑0.3秒、但每秒执行几百次的查询。我的习惯是先降到0.5秒,跑满一个业务高峰再回头看。
日志出来之后不要逐条读,用mysqldumpslow按总耗时排序,或者pt-query-digest出报告。重点看两类:执行次数最多的,和单次耗时最长的。前者改一次收益放大几百倍,后者往往是缺索引或者锁等待。
- slow_query_log = ON,slow_query_log_file 指到独立盘,别和data目录抢IO
- long_query_time 先设 0.5,稳定后再往 0.1 收紧
- log_queries_not_using_indexes 别常开,小表全扫也记,日志会被灌爆
这一步的代价是磁盘写放大。日志盘用机械盘的话,高峰期本身就会拖慢IO,所以要么单独挂一块SSD,要么只在排查窗口开。
EXPLAIN 只看两列就够定方向
拿着慢SQL去EXPLAIN,输出十几列,新手容易看花。我一般先盯type和rows。type出现ALL就是全表扫描,出现index是全索引扫描,这两种都说明索引没吃上;理想状态是ref、range或const。
rows是估算的扫描行数,如果它和实际返回行数差一个数量级,说明统计信息过期,ANALYZE TABLE先跑一遍再判断。Extra里出现Using filesort或者Using temporary,通常意味着排序或分组没走索引。
索引不是越多越好。一个写多读少的表,每加一个二级索引,INSERT就要多维护一棵B+树。我见过一张订单表挂了9个单列索引,写入延迟从8ms涨到40ms。复合索引要按最左前缀原则排字段,把区分度高的放前面。
如果SQL层已经压到极限,rows降到几十、type都是ref,CPU还是满,那问题就不在SQL了,往下走。
参数和硬件:钱该花在哪一步
innodb_buffer_pool_size是InnoDB最值钱的参数。经验值是给物理内存的60%到70%,一台8G内存的机器给5G到6G比较稳,剩下的留给连接、排序缓冲和系统本身。设太大触发swap,性能会断崖式下跌。
另外两个常被忽略的:innodb_log_file_size调大能减少checkpoint频率,但恢复时间会变长;max_connections设到几千没有意义,连接本身占内存,真正该做的是在上游加连接池。
到了这一步,如果buffer pool命中率还在95%以下、磁盘IO持续跑满,那就是内存或磁盘到顶了。加内存的效果通常立竿见影,加CPU往往没用——MySQL多数慢场景是IO等待而不是计算瓶颈。
机器层面的选择上,我倾向于给数据库单独一台物理机,不和Web应用混跑。像 马来西亚服务器6 这种 AMD EPYC 7542 双路、128G 内存的配置,跑中等规模库时buffer pool能给到80G,命中率基本稳定在99%以上,磁盘IO也不会被同机的其它进程抢。如果数据量和并发再上一档,马来西亚大带宽服务器 XXI 的256G内存和G口带宽更适合做主从架构里的主库。贵是贵,但比起每周一次半夜被慢查询告警叫醒,这笔钱花得值。
顺序记牢:先看慢日志定位SQL,再用EXPLAIN确认索引,SQL压不动了才动参数,参数调完还满再考虑加内存换机器。跳步的结果通常是钱花了、问题还在。