上周,一个做电商的朋友跟我抱怨,说他们的后台系统一到晚上七八点就卡得不行,查个订单要等十几秒,客服那边急得直跳脚。

我问他数据库用的什么,他说 MySQL,平时没感觉慢,就是最近流量涨了以后,越来越扛不住。我一听就笑了,这不是 MySQL 不行,而是根本没调教好。很多公司都是这样,项目初期随便建个表、写个查询,能跑就行,等用户量上来,问题全暴露了。
MySQL 优化这事,说白了就是三个字:别偷懒。你偷懒一次,后面得花十倍的力气去补坑。
先说说最基础的索引。很多人觉得索引就是给主键加一个,或者哪个字段经常被查就加哪个,这想法太天真了。索引不是越多越好,也不是随便加的。比如你有个用户表,里面存了几十万条记录,你经常用 去查,但 name 字段上没索引,MySQL 就得一条一条从头扫到尾,这叫全表扫描。几十万条数据还好,要是几百万、几千万,速度直接崩。
如果给 name 加个索引,MySQL 就能通过 B+树结构快速定位到数据,速度提升几百倍。不过有个坑要注意:索引虽然能加速查询,却会拖慢写入和更新。每多一个索引,INSERT 或 UPDATE 时,MySQL 都要多维护一棵树。所以索引不是无脑堆,得看业务场景——读多写少可以多建几个,写多读少则少建一些。
再说说查询语句的写法,这个真的让人头疼。我见过太多人写 SQL,习惯用 ,觉得省事。但你想想,一张表有几十个字段,你只需要其中两个,结果 MySQL 把整行数据都拉出来,浪费 IO 和内存。正确的做法是只查需要的字段,比如 ,少查一个字段就少一次数据传输。
还有更隐蔽的坑: 这种模糊查询,百分之百不走索引,因为 MySQL 不知道从哪开始匹配。必须使用模糊查询时,可以考虑全文索引或 Elasticsearch。另外, 条件也挺坑的,如果两边的字段没有索引,MySQL 可能直接放弃索引,改走全表扫描。解决办法是改写为 ,或者确保两边都有索引。
我有个朋友之前写了个报表查询,里面用了三个子查询嵌套,跑一次要两分钟。后来我帮他把子查询改成 JOIN,时间直接降到两秒。子查询本身没问题,但 MySQL 对子查询的优化有时很笨,会先执行外层再执行内层,导致大量数据扫描。相比之下,JOIN 通常让优化器更聪明地选择执行路径。
表结构设计这块,很多人一开始就没想清楚。比如字段类型选得太随意:该用 INT 的用了 VARCHAR,该用 DATETIME 的用了字符串。一个 VARCHAR 字段存数字,MySQL 每次比较都要先做类型转换,效率自然低。还有那种存 JSON 的场景,非要用 TEXT 字段存,然后在程序里自行解析,这不叫灵活,叫给自己挖坑。MySQL 5.7 以后已经原生支持 JSON 类型,查询和索引都方便得多。
分表分库是个经典话题。很多人一听分表就觉得是神器,其实不是所有场景都需要。如果单表数据量在千万级别以下,索引优化得好,MySQL 完全扛得住。非要去分表,反而会增加业务逻辑的复杂度,跨表查询、分页排序都成了噩梦。真正需要分表的场景,是单表数据量上亿,或者写入并发太高导致锁竞争严重。这时可以按时间、按用户 ID 取模等方式分表,或使用 MySQL 自带的分区表功能,把数据物理上分散到不同文件,但逻辑上仍是一张表。
缓存层也是很多人忽略的点。即使数据库再优化,也扛不住每秒几万次的查询。正确的思路是在数据库前面加一层缓存,比如 Redis 或 Memcached。热点数据放缓存里,数据库只处理缓存未命中的请求,这样压力能降一个数量级。但缓存有个坑:数据一致性。更新了数据库而缓存没同步,用户看到的就是脏数据。解决办法不复杂,要么给缓存设置合理的过期时间,要么在更新数据库时同步删除缓存,下一次查询再重新加载。
说说硬件和配置。很多人觉得数据库慢就是代码问题,其实有时候是服务器配置太低,或者 MySQL 参数没调。比如 决定了 InnoDB 能使用多少内存来缓存数据和索引。如果机器有 64 GB 内存,但这个参数只设了 1 GB,MySQL 大部分时间都在磁盘上读数据,速度自然慢。一般建议设成物理内存的 70%~80%,但别超过,留点空间给操作系统。
磁盘类型也很关键。机械硬盘的 IOPS 只有几百,而 NVMe SSD 的 IOPS 能达到几十万,差距是数量级的。业务对延迟敏感(比如金融、电商),强烈建议使用 SSD。
日志文件的配置同样不能忽视。Redo log、binlog 写得太频繁或文件太小,会导致频繁刷盘,影响性能。一般建议每个 redo log 文件设为 1 GB,整体保持 2~4 个,这样可以减少日志切换的频率。
还有一个容易被忽略的点:慢查询日志。很多 DBA 从不打开这个功能,出了问题才手忙脚乱。只要在配置文件里加上 和 ,就能把执行超过 2 秒的查询记录下来。定期查看,哪些 SQL 频繁上榜,针对性地优化,效果立竿见影。
我见过最夸张的案例是某公司一个查询跑了 30 秒,打开慢查询日志一看,里面有个 。该函数会为每一行生成随机数再排序,数据量大时直接卡死。改成在程序里随机生成 ID 再查询,时间立刻降到毫秒级。
说到底,MySQL 优化不是一次性工程,而是持续迭代的过程。业务在变,数据量在涨,优化策略也得跟着调整。今天加个索引,明天改写查询,后天再加缓存,慢慢把系统调到相对稳定的状态。
但有一点必须记住:不要为了优化而优化。有些团队看到索引能提速,就给所有字段都加上,结果写入性能掉了一半。还有人喜欢在代码里写一堆复杂的 SQL,觉得这样显得技术牛,其实完全可以用简单查询配合程序逻辑来替代。优化之前,先搞清楚瓶颈在哪——是 CPU 满了、内存不够、磁盘 IO 卡住,还是 SQL 本身写得烂?拿数据说话,别凭感觉盲目折腾。
说句实在话:MySQL 本身是个非常成熟的产品,大多数慢的问题都不是 MySQL 的锅,而是使用者没有用好。只要把索引、查询、表结构、缓存、配置这五个方面做到及格线以上,99% 的场景都能应付。剩下的 1% 可能需要从架构层面升级,比如读写分离、分库分表,甚至更换数据库。
所以,下次遇到数据库慢,别急着甩锅,先打开慢查询日志,看看那些慢 SQL 长什么样。很多时候,问题就藏在你习以为常的 和 里。


