1. 事故全景:从告警到定位
1.1 事故时间线
timeline title 故障时间轴 00:00 : 监控系统首次告警 00:05 : 数据库连接池使用率突破90% 00:08 : 网关开始出现503错误 00:12 : 自动扩容触发 00:15 : 人工介入排查
1.2 关键指标异常
指标 | 正常值 | 故障值 | 超出阈值 |
---|---|---|---|
接口 | p99 | 响应时间 | 200ms |
数据库 | qps | 800 | 3500 |
活跃连接数 | 20 | 200(max) | 10x |
1.3 排查工具链
// 监控工具清单 public class troubleshootingtools { string[] tools = { "skywalking 8.7.0", "arthas 3.6.7", "prometheus + grafana", "mysql slow query log" }; }
2. 深度剖析:mysql 分页查询的致命陷阱
2.1 offset 分页的执行原理
性能消耗公式:
总成本 = 全表扫描成本 + 排序成本 + 跳过行成本
2.2 索引失效的根本原因
-- 问题sql示例 explain select * from member_info where status = 1 order by create_time desc limit 10000, 20;
执行计划关键解读:
- type: all:全表扫描
- rows: 1250000:扫描行数
- extra: using filesort:无法利用索引排序
2.3 深度优化方案对比
方案一:游标分页(推荐)
-- 优化后sql(基于id分页) select * from member_info where status = 1 and id > #{lastid} order by id asc limit 20;
方案二:覆盖索引优化
-- 新增复合索引 alter table member_info add index idx_status_createtime (status, create_time); -- 改写sql select * from member_info where status = 1 order by create_time desc limit 20;
方案对比表
方案 | 扫描行数 | 排序方式 | 适用场景 |
---|---|---|---|
原始offset | 10020 | 文件排序 | 小数据量 |
游标分页 | 20 | 索引排序 | 大数据量、深度分页 |
覆盖索引 | 20 | 索引覆盖 | 中等数据量 |
3. 完整优化实战
3.1 mybatis 改造示例
public interface membermapper { // 旧方法(问题代码) @select("select * from member_info where status = #{status} limit #{offset}, #{limit}") list<member> listbypage(@param("status") int status, @param("offset") int offset, @param("limit") int limit); // 新方法(优化后) @select("select * from member_info where status = #{status} and id > #{lastid} order by id asc limit #{limit}") list<member> listbycursor(@param("status") int status, @param("lastid") long lastid, @param("limit") int limit); }
3.2 服务层改造
public pageresult<member> getmemberlist(int pagesize, long lastid) { // 游标分页查询 list<member> members = membermapper.listbycursor(1, lastid, pagesize); // 获取下一页的游标 long nextlastid = members.isempty() ? null : members.get(members.size()-1).getid(); return new pageresult<>(members, nextlastid); }
4. 防御体系:慢查询防控全景方案
4.1 事前预防
4.2 事中监控
# my.cnf 慢查询配置 slow_query_log = 1 slow_query_log_file = /var/log/mysql/mysql-slow.log long_query_time = 1 log_queries_not_using_indexes = 1
5. 优化效果验证
5.1 压测数据对比
场景 | tps | 平均响应时间 | 错误率 | cpu使用率 |
---|---|---|---|---|
优化前 | 85 | 6100ms | 32% | 96% |
优化后 | 2150 | 230ms | 0% | 45% |
6. 工程师的自我修养
6.1 sql 编写军规
- 禁止 无限制的 select *
- 必须 为分页查询添加 order by
- 推荐 使用游标替代 offset
- 强制 为 where 条件字段建立索引
总结
到此这篇关于java慢查询排查与性能调优的文章就介绍到这了,更多相关java慢查询排查与性能调优内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论