上周帮一个朋友排查他的电商网站,打开后台,那个慢啊——加载一个订单列表要等十几秒,客服都急得拍桌子。我一看 MySQL 的慢查询日志,好家伙,一堆全表扫描,索引根本没用上。这种场景太熟悉了,几乎每个开发都碰到过:数据库慢,业务卡,老板催。MySQL 优化这事儿,说简单也简单,说复杂也复杂,关键是要知道从哪儿下手。

先聊索引,这是最基础也最容易被搞砸的地方。很多人觉得建了索引就万事大吉,其实索引不是越多越好。我见过一个项目,一张表上挂了十几个索引,每次插入数据都要更新索引树,写入性能直接腰斩。索引的设计要遵循“最左前缀原则”,比如你查询 ,建一个 联合索引就够了,别单独给 a 和 b 各建一个。还有人喜欢在 这种模糊查询上建索引,结果索引根本用不上,等于白建。真正管用的做法是:先分析慢查询日志,找出执行时间长、扫描行数多的 SQL,然后针对性地加索引。索引字段的选择也有讲究,区分度高的列优先,比如用户 ID、订单号,别在只有两个值的性别列上浪费空间。
索引搞定了,SQL 写法又是另一个大坑。很多人写 SQL 就像写散文,想到哪儿写到哪儿。比如 这种子查询,MySQL 优化器处理起来很吃力,往往要走全表扫描。改成 或者 ,性能能提升一个数量级。还有在 条件里对索引列做函数操作的,比如 ,索引直接失效,得写成 。这些细节看着不起眼,但积少成多,对性能影响巨大。我见过最夸张的案例,一个查询改写后,从 30 秒降到 0.1 秒,开发同学当场愣住。
说完 SQL,再说说表结构和数据类型。很多新手喜欢用 存所有字符串,不管实际数据多短。但 MySQL 处理变长字段是有代价的,临时表排序、内存分配都会受影响。能存整数的就别用字符串,能用 的就别用 ,能用 的就别用 。比如存储 IP 地址,很多人用 ,其实用 配合 和 ,存储空间从 15 字节降到 4 字节,查询速度还更快。还有那种一张表几千个字段的设计,看着就让人头疼。数据库设计的核心是“高内聚、低耦合”,一张表只存该存的东西,别把业务逻辑都塞进去。
分区表也是个好东西,但很多人用错了。分区不是用来加速单条查询的,而是用来管理海量数据的。比如日志表,按月份分区,查询最近一个月的数据时,MySQL 只需要扫描一个分区,而不是全表。但分区键的选择很关键,别选区分度不高的列,比如按年份分区,一年一个分区,数据量仍然很大。更常见的问题是,跨分区查询时性能反而比普通表差。所以分区前要想清楚:你的查询模式是什么?是否经常按分区键过滤?如果不是,就别盲目分区。
缓存层的作用容易被低估。很多人觉得数据库优化就是调 SQL、加索引,其实业务层加个缓存能解决大部分问题。比如商品详情页,每秒上千次请求,每次都查数据库,再好的机器也扛不住。加个 Redis,把热点数据缓存起来,数据库压力瞬间下降。缓存策略要设计好,别搞“缓存穿透”或“缓存雪崩”。比如商品信息,缓存失效时加个互斥锁,避免大量请求同时打到数据库。还有些场景可以用本地缓存,比如配置信息,变化频率极低,放在内存里就行了,连 Redis 都不必。
硬件和配置层面的优化也不能忽视。很多人买服务器只关注 CPU 和内存,硬盘随便选个机械盘。但 MySQL 的 IO 操作很频繁,尤其是写操作,机械盘的随机读写性能差得离谱。换成 SSD,同样的 SQL,性能能提升好几倍。配置方面, 最关键,它决定了 InnoDB 能使用多少内存来缓存数据和索引。一般建议设置为物理内存的 70%~80%,但别超过 80%,要给操作系统留点余量。 在 MySQL 8.0 已经废弃,但很多人仍在使用老版本的查询缓存,结果在高并发场景下反而变慢,因为缓存失效的锁竞争太严重。
聊点实践中的坑。一次我帮一个金融项目做优化,他们的统计报表每天跑一次,需要两个小时。我一看 SQL,里面有个 用来随机抽样。这个函数在数据量大时性能极差,因为要对所有数据排序。改成程序随机生成 ID 再查询,时间从两小时降到两分钟。还有常见的 分页问题,当偏移量很大时,比如 ,MySQL 要扫描前 10 万行再丢弃,性能极差。改用子查询优化或游标分页,就能避免这个问题。优化没有银弹,每个场景都要具体分析,但核心思路一致:减少扫描行数,减少 IO 次数,让数据库去做它擅长的事。
说实话,MySQL 优化靠的不是技巧,而是对业务的理解。你得知道哪些数据是热数据,哪些查询是高频查询,哪些场景可以接受延迟。比如电商网站,商品详情页的缓存可以设置 5 分钟失效,但订单状态的更新必须实时。这些判断没有标准答案,全靠经验。所以别指望看几篇文章就能成为优化高手,多动手、多踩坑、多复盘,慢慢就有感觉了。数据库这东西,你对它好,它就对你快。


