MySQL调优从哪下手:慢查询、索引与机器配置的取舍顺序
库跑得慢的时候,多数人第一反应是去翻 SQL,把每条慢查询都加个索引。这个方向不算错,但顺序经常是反的。一台 4 核 8G 的机器上,innodb_buffer_pool_size 默认值往往只有 128M,数据全在磁盘上打转,这时候你把索引调得再漂亮,每次查询还是要走磁盘 IO。我一般会先问一句:这台机器多大内存、盘是机械还是 SSD。这两个问题的答案,基本决定了后面该调参数还是该换机器。
先看 innodb_buffer_pool_size,它比索引更影响体感
缓冲池是 InnoDB 缓存数据和索引的地方,热数据命中它就不碰磁盘。专用数据库服务器上,通常可以给到物理内存的 50% 到 70%。一台 64G 内存的机器,给 40G 左右是常见做法;留出余量是因为连接、排序、临时表也要吃内存。
这个值设小了,最直观的表现是磁盘读持续偏高,慢查询日志里大量查询卡在 IO 等待。设太大则可能触发 swap,反而更慢。改完要重启实例,生产环境得挑低峰期。
还有个容易忽略的点:缓冲池预热。重启之后缓存是空的,前几十分钟性能会明显低于常态,这段时间的监控数据别拿来下结论。
如果内存本身就只有 8G、16G,把缓冲池调到 10G 也只是把磁盘压力挪到 swap 上。这种情况下,加内存或者换一台内存更大的独立服务器,比继续抠参数划算。按我的经验,数据量超过内存三倍之后,参数优化的边际收益掉得很快。
慢查询日志怎么读,先解决影响面最大的那几条
开慢查询日志是第一步,long_query_time 从 1 秒起步就行,别一上来设 0.1,否则日志量大到没法看。跑一两天,用 mysqldumpslow 或 pt-query-digest 聚合一下,按总耗时排序,而不是按单条耗时排序。
一条执行 5 秒但一天只跑一次的报表查询,和一条执行 0.3 秒但每秒跑几十次的接口查询,后者对整体吞吐的伤害大得多。
- 先看扫描行数:rows_examined 远大于返回行数,说明索引没走对或没走全
- 再看是否出现 Using filesort、Using temporary,这两个通常意味着排序或分组没吃到索引
改完一条就复测一次,别攒一批一起改。攒着改,出了问题分不清是哪条引起的。
索引的字段顺序,比加不加索引更关键
联合索引遵循最左前缀,这个规则都知道,但落到具体字段顺序上还是容易排错。原则是等值条件放前面,范围条件放后面。比如查询里同时有 status 和 created_at,status 是等值、created_at 是范围,索引建成 (status, created_at) 才能让范围条件也吃上索引;反过来建成 (created_at, status),范围之后的字段就用不上了。
覆盖索引值得单独提一句。如果查询需要的字段都在索引里,引擎不用回表,速度差别在数据量大时相当明显。代价是索引占空间变大,写入也要维护更多索引。
索引不是越多越好。每个索引都会拖慢写入,一张写入频繁的表上挂七八个索引,插入性能会明显下滑。我见过不少配置是历史遗留索引堆着没人清,定期用 sys.schema_unused_indexes 查一下没被用到的索引,删掉能省不少写入开销。
关于硬件这一层,如果排查下来瓶颈确实在磁盘随机读,机械盘换 SSD 带来的提升往往比继续调 SQL 大。像马来西亚服务器3这类 E5-2698v4*2 / 64G 的配置,内存够把缓冲池开到 40G 上下,适合中等数据量的业务库;数据量再大、并发再高的场景,可以考虑马来西亚服务器7,AMD EPYC 7742 双路加 256G 内存,缓冲池能覆盖的数据集大得多。
什么时候该停手,直接换机器
调优有个明确的止损点:如果缓冲池已经给到内存上限、索引也梳理过一遍,慢查询数量还是压不下去,那问题多半不在 SQL 层。
常见的三种情况,继续调参数基本没用:
- 数据量远超内存,每次查询都要回磁盘随机读
- 写入量太大,单机磁盘 IOPS 已经打满
- 连接数持续上千,CPU 长期跑在高位
这时候把钱花在内存和磁盘上,比花在 DBA 工时上回报高。换我我会先租一台内存更大的机器跑一个月,把真实负载压上去看数据,再决定要不要长期上。数据量到几百 G、并发写入又高的库,单机再怎么调都有天花板,读写分离或者分库分表是绕不过去的下一步。
结论很直接:内存 8G 以下的库,先加内存;缓冲池没给够的,先把参数调到位;索引乱建一堆的,先做减法。这三步做完还有明显慢查询,就该考虑换整机或者拆库了。别在参数里绕太久。