凌晨 2:00,报警狂响:CPU 100%、页面卡成 PPT。慢查询日志里,一条 45 秒 的“祖传 SQL”正吞噬着数据库。你冷静下来,打开 EXPLAIN,看到 type: ALL、Extra: Using filesort,一条联合索引顺序不当,直接把查询拖进泥潭。十分钟后,索引重排、SQL 改写、配置微调,同样的请求稳定在 0.8 秒。这不是玄学,而是可复制的 MySQL 性能优化 方法论。

优化优先级与总体思路优先顺序:SQL 与索引 > 表结构 > 配置参数 > 硬件与架构。小改动撬动大收益,先拿“低垂的果实”。四个“减少”:减少数据访问(索引/压缩)、减少返回数据(只查需要的列/分页)、减少交互次数(批量/合并)、减少服务器开销(避免排序/全表)。三个“利用”:利用索引覆盖、利用内存缓存、利用并行/分区。一句话铁律:没有银弹,用数据说话(监控 + EXPLAIN 验证)。SQL 与索引的十个快招只查需要的列:避免 SELECT*,减少 IO 与网络。联合索引顺序要对:遵循“等值列在前,范围列在后”,让索引物尽其用。避免索引失效:不要在 WHERE 中对列做函数/计算,如 WHERE YEAR(created_at) = 2024会扫全表。模糊查询别前置:LIKE '%abc'难用索引;改为 LIKE 'abc%'或 全文索引。子查询改 JOIN:多数场景 JOIN 比 IN (子查询) 更高效,驱动表要小。分页用“游标分页”:基于有序主键/索引列 WHERE id > ? ORDER BY id LIMIT N,告别 OFFSET 100000。覆盖索引优先:让查询列都在索引里,避免回表。排序/分组要索引:为 ORDER BY/GROUP BY 建立合适索引,消除 Using filesort/Using temporary。合理冗余与拆分:把大字段/低频更新字段拆到扩展表,降低锁与 IO。定期清理无用索引:索引是“双刃剑”,写多时维护成本显著。

表结构与存储的硬功夫选对数据类型:能用 TINYINT 不用 INT,能用 VARCHAR(50) 不用 TEXT,既省空间又提速。主键设计:优先 自增 BIGINT 或业务无关的 UUID(配合有序 UUID 方案),避免随机主键导致页分裂。垂直拆分:把“热字段”与“大字段”分离,主表轻装上阵,更新不互相拖累。水平拆分/分区:按 时间/租户/地域 切分,查询只扫相关分区,维护更可控。存储引擎:优先 InnoDB(事务、行锁、MVCC),MyISAM 仅适合读多写少且不需事务的场景。字符集与校对:统一 utf8mb4,避免隐式转换导致索引失效。配置与架构的进阶打法内存是命门:把 innodb_buffer_pool_size 调到物理内存的约 70%~80%(专用库),让更多数据/索引留在内存。连接与线程:合理设置 max_connections 与 thread_cache_size,避免频繁建连与线程抖动。临时表与磁盘:把 tmpdir 指向 SSD 或大容量盘,避免大排序/哈希聚合撑爆系统盘。读写分离与缓存:读多写少场景用 主从复制 + 读写分离,热点数据上 Redis/Memcached。云上要点:优先 SSD、合理内存、监控慢查询与连接数,按需开启/调优缓存策略。监控与慢查询:开启 slow_query_log,用 pt-query-digest 聚合分析,定位 Top SQL 逐个击破。

一套可复制的优化清单第一步:打开 slow_query_log,收集 1~3 天 真实流量 SQL。第二步:用 EXPLAIN 看 type、key、rows、Extra,优先消灭 ALL 与 Using filesort。第三步:为高频 SQL 建立“最小够用”的联合索引,优先覆盖索引。第四步:改写 SQL(游标分页、子查询改 JOIN、避免函数包列)。第五步:调参(先 innodb_buffer_pool_size,再连接/临时表/排序缓冲)。第六步:压测验证(QPS、P95/P99、错误率、磁盘 IO、连接数)。第七步:上线灰度与回滚预案,持续监控与二次迭代。避坑与提醒别盲加索引:写放大、锁竞争、统计信息失真都会反噬性能。慎用 STRAIGHT_JOIN:仅在明确“小表驱动大表”且优化器失准时使用,版本升级可能失效。分区不是银弹:设计不当会引入 分区裁剪失效 与维护复杂度。变更先备份:任何 DDL/参数调整先在测试环境演练,准备好回滚方案。云盘不是无限快:合理 IOPS/吞吐 规划,避免临时表/排序成为瓶颈。附:关键 SQL 示例联合索引与覆盖索引-- 示例:按城市查订单并按时间倒序CREATE INDEX idx_city_orderdate ON orders (city, order_date);-- 覆盖索引:查询列都在索引中,避免回表SELECT order_id, customer_id, order_dateFROM ordersWHERE city = 'Beijing'ORDER BY order_date DESCLIMIT 100;游标分页(深度分页优化)-- 上一页最后一条 id = :last_idSELECT id, titleFROM articlesWHERE id > :last_idORDER BY idLIMIT 20;子查询改 JOIN-- 慢:子查询SELECT *FROM users uWHERE u.id IN (SELECT user_id FROM orders WHERE amount > 1000);-- 快:JOINSELECT DISTINCT u.*FROM users uJOIN orders o ON u.id = o.user_idWHERE o.amount > 1000;模糊查询优化-- 前缀匹配,走索引SELECT * FROM users WHERE name LIKE '张%';-- 全文检索(MyISAM/InnoDB 支持,中文需分词器或改用 ES)SELECT * FROM articlesWHERE MATCH(title, content) AGAINST('数据库优化' IN NATURAL LANGUAGE MODE);结语
性能优化不是一次性工程,而是“监控—定位—验证—迭代”的循环。用 EXPLAIN 做 X 光,用 慢查询日志 做体检,用 合理索引 做手术刀,再辅以 配置与架构 的加固,你的 MySQL 也能从“老牛拉车”进化成“高铁狂飙”。
还没有评论,来说两句吧...