做数据库调优这行当久了,你会发现一个特别有意思的现象:很多人一遇到MySQL变慢,第一反应就是加机器、加内存、加SSD,好像钱花到位了,性能就上来了。但等你真正去扒他的慢查询日志,翻他的表结构,看他的执行计划,八成会发现——瓶颈根本不在硬件上,而在那些年你随手写下的SQL、随手建的表、随手定的索引策略上。MySQL性能飙升这件事,从来不是靠堆配置堆出来的,而是靠你把每一个细节抠到极致,把每一分资源用到刀刃上。今天这篇全攻略,咱们不聊虚的,直接上实战,从最基础的配置到最刁钻的索引陷阱,一条条给你捋清楚。

先说说配置参数。很多人拿到一台新服务器,默认配置就敢直接上生产,这跟穿着拖鞋去跑马拉松没什么区别。MySQL的默认配置是按“最小可用”标准设计的,它要保证在任何垃圾机器上都能跑起来,所以innodbbufferpoolsize默认只有128M,maxconnections默认只有151。你想象一下,你的业务高峰期同时有500个连接进来,151个名额早就被占满了,剩下的请求全在排队等,能不慢吗?实战里,我一般建议先把innodbbufferpoolsize调到物理内存的60%到70%,如果你的机器有64G内存,那就给InnoDB留个40G左右。这个参数是MySQL的命脉,它决定了你的热点数据能有多少留在内存里,而不是频繁去磁盘上翻。另外,innodblogfilesize也别太小,默认的48M在写入量大的时候会频繁触发checkpoint,导致磁盘IO飙高,我通常直接调到1G以上。还有maxconnections,别傻乎乎地调到几千,连接数越高,每个连接消耗的内存就越多,反而容易把机器拖垮。你要做的是先看你的实际连接数峰值,然后在这个基础上加个20%到30%的余量,比如你的峰值是300,那就设个400,够用就行。
配置调完了,咱们来说说最容易被忽视的杀手——慢查询日志。你连自己的敌人是谁都不知道,怎么打仗?MySQL默认是关闭慢查询日志的,你得手动打开,而且不光是打开,还得把阈值设得合理。我见过有人把longquerytime设成10秒,结果日志里半年都蹦不出一条记录,这跟没开有什么区别?我一般建议设成1秒,甚至0.5秒,宁可日志多一点,也别漏掉那些“温水煮青蛙”式的慢SQL。打开之后,你要做的是定期去分析这些日志,看看哪些SQL是高频慢查询,哪些是低频但单次执行时间特别长的。这里有个小技巧,你可以用mysqldumpslow这个工具把日志按执行次数和总耗时排序,优先处理排名靠前的。别等到用户投诉了才想起来去看日志,那会儿黄花菜都凉了。
说完了日志,咱们聊聊索引。索引这东西,用好了是神器,用不好就是灾难。很多新手喜欢给每个字段都加上索引,觉得索引越多查询越快,这完全是误区。索引的本质是用空间换时间,每多一个索引,写入的时候就要多维护一颗B+树,你的INSERT和UPDATE性能就会下降一分。而且,索引不是建了就一定能用上的,MySQL的优化器有自己的判断逻辑,你建了三个单列索引,它可能一个都用不上,反而去走全表扫描。实战中,我特别推崇联合索引,也就是复合索引,它能同时覆盖多个查询条件。比如你有个订单表,经常按userid和status两个字段一起查,那就建一个(userid, status)的联合索引,注意顺序很重要,最左前缀原则,把区分度高的字段放前面。另外,要警惕那些“索引失效”的场景,比如你在查询条件里对索引字段用了函数,像WHERE DATE(createtime) = '2024-01-01',这就废了索引,应该改成范围查询,比如WHERE createtime >= '2024-01-01' AND createtime < '2024-01-02'。这种细节,不踩几次坑你是记不住的。
接下来咱们聊聊SQL写法,这可能是调优里性价比最高的部分。同样的查询结果,不同写法性能能差出几十倍。我见过最典型的问题是SELECT *,这玩意儿在开发环境跑着挺爽,到了生产环境数据量一大,光是把那些用不上的大字段(比如TEXT、BLOB)从磁盘读出来就够你喝一壶的。正确做法是只select你需要的字段,能用索引覆盖的绝不回表。还有,要尽量避免在WHERE子句里用OR连接多个条件,比如WHERE status = 1 OR status = 2,这种写法MySQL很难走索引,你不如拆成两个查询然后用UNION ALL合并,或者用IN (1,2)来替代。再比如,LIMIT分页在深翻页的时候有个大坑,你写LIMIT 100, 20,MySQL得先扫描前十万行再丢掉,这性能能好才怪。解决办法是记录上一页的最大ID,然后用WHERE id > 上一页最大ID LIMIT 20,这种“延迟关联”或者“书签”的方式,能直接把查询时间从秒级降到毫秒级。
表结构设计这块,很多人一开始就没打好地基。MySQL的表设计,说穿了就是三个字:反范式。什么意思?就是别太迷信数据库的三大范式,该冗余的字段就冗余,该拆的表就拆。比如你有个用户表和订单表,订单表里非要存用户的详细地址、手机号、昵称这些信息,每次查询都得JOIN一下用户表,这多累啊。你不如在订单表里直接冗余一份手机号和昵称,查询的时候连JOIN都省了。当然,冗余的前提是你能接受数据的一致性风险,比如用户改了昵称,历史订单里的旧昵称不更新,这通常是可以接受的。另外,字段类型的选择也有讲究,能用INT就别用VARCHAR,能用DATETIME就别用TIMESTAMP(除非你有时区需求),能用TINYINT就别用INT。我见过有人用VARCHAR(255)来存性别,一个字节能搞定的事非要占255个字节,这不是浪费是什么?数据量一大,这些浪费全都会变成磁盘IO和内存的负担。
说完了表结构,咱们得聊聊分区表和分库分表。很多人在数据量到了千万级别的时候就开始慌,觉得MySQL撑不住了,其实很多时候是没用好分区。分区表的好处是能让MySQL在查询时自动裁剪掉不需要的分区,比如你按时间分区,查最近一个月的数据,MySQL只需要扫描这一个分区,而不是整张表。但分区表也有坑,比如分区键必须包含在主键里,不然会报错,而且分区数量不是越多越好,太多反而会降低性能。如果你的数据量真的到了单表几亿行,那分区表也不够了,这时候就得考虑分库分表。分库分表是个大工程,需要引入中间件(比如ShardingSphere、MyCat),还得处理分布式事务、全局ID生成这些复杂问题。我的建议是,没到万不得已别碰分库分表,先用分区表顶着,实在顶不住了再上,而且一定要把拆分策略提前规划好,别等业务跑起来了再重构,那代价你承受不起。
咱们聊聊监控和持续优化。调优不是一锤子买卖,你得建立一套监控体系,时刻盯着MySQL的健康状况。我常用的工具是Performance Schema和sys schema,这些MySQL自带的性能分析工具,能看到哪些SQL在消耗最多的IO、哪些锁在阻塞、哪些临时表在磁盘上创建。还有一个特别实用的指标是QPS和TPS,这俩能直观反映你的数据库在忙什么。如果你发现QPS很高但TPS很低,说明查询多写入少,那你的索引和缓存策略就要侧重读优化;反过来,如果TPS很高,那你就得关注写入性能,比如binlog刷盘策略、redo log的大小这些。另外,别忘了定期做一次EXPLAIN分析,把你线上最频繁的几条SQL拿出来,看看它们的执行计划有没有变化,索引有没有被正确使用。MySQL的优化器有时候挺“任性”的,同一个SQL,数据量变了,它可能就换执行方式了,你不定期检查,就等着某天突然线上告警吧。
回到开头那句话,MySQL性能飙升从来不是什么玄学,它是一套系统工程,从配置到索引,从SQL到表结构,从监控到迭代,每一步都得踏踏实实做好。我见过太多团队,一遇到性能问题就想着加机器、换中间件,却从不肯静下心来分析自己的SQL和表设计,这完全是本末倒置。其实大部分性能问题,根源都在开发阶段埋下的雷——索引乱建、SQL写得太随意、表结构设计不合理。把这些雷一个个排掉,你的MySQL不需要昂贵的硬件,也能跑出让人满意的速度。记住,调优这件事,永远没有终点,你的业务在变,数据量在变,访问模式也在变,所以持续监控、持续优化才是王道。别指望一劳永逸,也别迷信什么“银弹”,脚踏实地把基本功练扎实,你就是那个能让MySQL性能飙升的人。


