MySQL调优别急着改参数:先分清慢在SQL还是慢在磁盘
数据库变慢的时候,多数人的第一反应是打开配置文件改参数。我见过不少机器,32G内存里buffer pool只给了8G,剩下全空着,可慢查询还是照样出现。问题往往不在参数上,而在于没人先弄清楚瓶颈到底在哪一层。
调优这件事有顺序。先看SQL本身,再看索引,然后才是内存和磁盘,最后才轮到参数微调。顺序反了,改了半天参数,慢查询还是那几条。
先看慢查询日志,别凭感觉猜
慢查询日志是唯一能告诉你真相的东西。开之前先在配置文件里设好long_query_time,通常从1秒开始,超过这个值的语句全记下来。
拿到日志之后重点看三样:慢查询出现的频率、单条SQL扫了多少行、有没有用到索引。一条SQL每秒跑几十次但每次只扫几百行,和一条SQL十分钟跑一次却扫了几百万行,处理方式完全不同。
- 频率高、扫描行数少:通常是索引不够精细,或者SQL写法有问题
- 频率低、扫描行数巨大:多半是缺索引,或者统计信息过期导致执行计划走偏
我一般会先跑一遍EXPLAIN,看type那一列是不是ALL或者index。出现ALL基本可以确定是全表扫描,这时候加参数没用,得动索引。
索引不是越多越好,加错比不加更糟
确认是索引问题之后,别急着见WHERE字段就加索引。一张表上挂了十几个单列索引,写入性能会明显下降,因为每次INSERT都要维护所有索引的B+树。
联合索引的顺序有讲究。WHERE条件里等值查询的字段放前面,范围查询的字段放后面。比如查询条件是status=1 AND created_at > '2026-01-01',索引建成(status, created_at)比反过来有效得多。
还有个容易忽略的点:索引列上用了函数或者隐式类型转换,索引直接失效。WHERE DATE(created_at) = '2026-01-01'这种写法,哪怕created_at上有索引也用不上,得改成范围查询。
加索引之前先用EXPLAIN验证一遍,确认key那一列真的用上了你建的索引。这一步花不了两分钟,但能省掉后面反复调整的时间。
内存和磁盘:调参解决不了IO瓶颈
索引都加对了,慢查询还在,那就要看innodb_buffer_pool_size够不够。这个参数决定了InnoDB能缓存多少数据和索引在内存里。热数据装得下,查询走内存;装不下,就得读磁盘。
一台32G内存的机器,如果只跑MySQL,buffer pool给到20G到24G是合理的。但光加内存不一定有用——如果数据量本身就有几百G,加再多内存也装不下全部热数据,这时候磁盘IO就是硬瓶颈。
机械硬盘的随机读通常在100到200 IOPS,SSD能到几万。这个差距调参数补不回来。我一般的判断是:如果iostat里磁盘util长期在80%以上,或者await明显偏高,那问题在存储层,换SSD或者拆库分表比改参数有效。
如果业务本身对数据库IO要求高,机器选型阶段就该考虑SSD和大内存。秀米云有马来西亚裸机云 IV,E5-2690配32G内存和20M带宽,月付124美元,跑中小型MySQL实例够用。数据量再大一些的,可以看马来西亚裸机云 XIII,双E5-2698v4加64G内存,月付219美元,buffer pool能给到40G以上。
连接数和临时表这些参数什么时候动
max_connections不用设太大。连接数上去之后每个连接都要占内存,设成几千反而容易把内存吃光。看Threads_connected的历史峰值,在这个基础上留一倍余量就够了。
tmp_table_size和max_heap_table_size决定内存临时表的大小。如果慢查询里出现大量Using temporary,可以适当调大,但别超过buffer pool的四分之一,不然会挤占缓存空间。
这些参数属于锦上添花。索引没加对、磁盘IO跟不上,参数调得再漂亮也没用。调优的顺序永远是:SQL写法 → 索引 → 内存 → 磁盘 → 参数。
回到开头那个场景:32G内存buffer pool只给8G,先别改这个参数。打开慢查询日志,跑一遍EXPLAIN,把全表扫描的SQL找出来加索引。索引加完还慢,再回来把buffer pool调到20G以上。如果调到24G还是慢,那就是数据量超过了单机内存能覆盖的范围,该考虑分库分表或者上SSD了。