实用科技屋
霓虹主题四 · 更硬核的阅读氛围

一次线上慢查询的救火经历:MySQL性能调优实战

发布时间:2025-12-18 07:41:15 阅读:283 次

上周三下午快下班时,客服突然喊了一声:‘后台订单列表打不开了!’ 刷新几次,页面动不动卡个五六秒。我们这系统平时响应都在200ms以内,明显不对劲。

从慢查询日志入手

登录服务器第一件事就是看MySQL的慢查询日志。果然,有几条执行时间超过2秒的SELECT语句,全集中在orders表。语句长这样:

SELECT * FROM orders WHERE user_id = 12345 AND status = 'paid' ORDER BY created_at DESC LIMIT 20;

看起来挺正常,但EXPLAIN一下就露馅了:

EXPLAIN SELECT * FROM orders WHERE user_id = 12345 AND status = 'paid' ORDER BY created_at DESC LIMIT 20;

结果显示key是NULL,rows扫了接近10万行。这张表现在有80多万数据,没索引可不就全表扫描了。

加复合索引,立竿见影

问题出在查询条件和排序字段上。user_idstatuscreated_at这三个字段得一起建个联合索引:

ALTER TABLE orders ADD INDEX idx_user_status_time (user_id, status, created_at);

索引建完再跑一遍EXPLAINrows降到几十行,执行时间从2秒多变成60毫秒。前端刷新,订单页秒开。

别只盯着索引,连接池也得管

过了两天,又发现高峰期偶尔报“Too many connections”。查了下max_connections设的是150,看着不少,但应用用的是PHP-FPM,默认每个请求都新建数据库连接。

改用持久连接还不够,还得控制FPM子进程数。把pm.max_children从50降到20,同时在MySQL里调高wait_timeout,避免空闲连接占着坑。

还有个小细节:text字段拖后腿

有次导出用户备注信息,发现一条SQL特别慢。语句只是查user_idnote,但note是TEXT类型,而且内容动辄几KB。

问题在于,即使只查两列,如果走的是主键索引回表,还是会把整行数据捞出来,包括那个大文本。解决方案是把note拆到单独的扩展表里,主表只留核心字段,查询轻快多了。

监控不能停

现在每天早上第一件事就是看前一天的慢查询报表。用pt-query-digest分析日志,配合Zabbix告警,新出现的慢SQL基本当天就能发现。

调优不是一锤子买卖,数据量涨了,业务逻辑变了,原来没问题的查询也可能变慢。就像家里水管,用久了得通一通,关键时候才不漏水。