《100个“反常识”经验09:一条慢查询拖垮整个库》
先唠两句大家好啊我是阿垚。欢迎来到《100个“反常识”经验》第09期。上期聊了被DDoS后的48小时应急。今天聊一个数据库方向的老大难问题一条慢查询拖垮整个库。你有没有遇到过数据库CPU突然飙到100%连接数爆满业务大面积超时。排查后发现罪魁祸首只是一条看似简单的SQL。这不是数据库的错是你对索引和查询计划的理解还不够深。一次让人后背发凉的故障周四下午业务高峰期。数据库报警CPU使用率持续100%。登录数据库查看当前正在执行的查询sqlSHOW PROCESSLIST;发现大量查询处于“Sending data”状态而且都在执行同一张订单表的查询sqlSELECT * FROM orders WHERE user_id 12345 AND status 1 ORDER BY created_at DESC;这张表有2000万行数据。user_id和status字段都没有索引。每次查询都要全表扫描排序更是噩梦。这条SQL是刚上线的一个新功能触发的QPS不算高只有几十但每次查询耗时2-3秒很快就把数据库的连接池和CPU打满了。慢查询为什么会拖垮整个库一条慢查询看似只影响自己但实际上会引发连锁反应占用数据库连接导致其他正常请求排队等待消耗大量CPU和IO拖慢所有查询引发连接风暴应用端不断重试进一步加剧压力可能导致主从延迟影响读写分离架构排查三步法第一步快速定位慢查询sql-- 查看当前正在执行的慢查询 SHOW FULL PROCESSLIST; -- 查看慢查询日志需提前开启 SET GLOBAL slow_query_log ON; SET GLOBAL long_query_time 1;第二步分析查询执行计划sqlEXPLAIN SELECT * FROM orders WHERE user_id 12345 AND status 1 ORDER BY created_at DESC;关注几个关键字段typeALL 表示全表扫描危险rows扫描的行数越大越慢ExtraUsing filesort 表示需要额外排序第三步验证索引效果sql-- 创建复合索引 CREATE INDEX idx_user_status_time ON orders (user_id, status, created_at); -- 再次执行EXPLAIN观察type和rows变化永久防范方案✅建立索引规范WHERE条件中的字段必须建索引高频查询的排序字段应包含在复合索引中避免在索引列上使用函数或计算✅开启慢查询监控sql-- 慢查询阈值设为1秒 SET GLOBAL long_query_time 1; -- 记录未使用索引的查询 SET GLOBAL log_queries_not_using_indexes ON;✅使用查询限流应用层对高风险接口进行限流和熔断数据库层使用pt-query-digest定期分析✅读写分离 分库分表将统计类、报表类查询分流到从库超千万级大表考虑按时间或业务键分表一键慢查询分析脚本bash#!/bin/bash # 分析最近1小时的慢查询日志 pt-query-digest --since 1h /var/log/mysql/slow.log slow_report.txt下期预告《100个“反常识”经验10主从延迟从10秒飙到10小时》评论区分享你遇到过的印象最深的慢查询案例。——阿垚一个踩过2万次坑的“老”IT