记一次生产环境CPU飙升100%的排查经历

一、前言

是这样的,我的项目是使用传统的Spring+SpringMVC+Mybatis架构的,有PC端后台管理系统,也有微信端H5网页,也就是一个微信公众号项目;项目部署采用Windows Server2008服务器,数据库使用·Mysql5.7·,最开始运行都非常的稳定,自动上线了一款功能:“微信打卡” 之后,规定每天只能打卡一次,并且规定时间点为6:00-9:00 这个时间段才能打卡,不在这个时间段则无法打卡,我的微信公众号用户有10万多,具体每天打卡量没有去具体的统计。

二、并发场景下产生的现象

6:00-9:00 这个时间段内,大量用户集中打卡,难免产生并发,服务器CPU持续飙升为100%,远程连接Windows服务器,鼠标卡死,无法操作,感觉整个系统瘫痪了,起初是以为生成的日志文件过多,于是每个月定时去删除上一个月的日志,最后发现不是,除了在6:00-9:00 这个时间段内外的其他时间,访问微信公众号H5网页,非常的流畅和稳定,页面响应也非常的快,只有在这个时间段内大量页面卡死,等待服务器响应,于是就有了后面的三次排查经历;

三、排查经历

1. 第1次CPU飙升100%的排查经历(未定位到核心)

第一次遇到这个问题,查看日志: Data source rejected establishment of connection, message from server : "Too many connections" 大概意思是连接数太多,于是我调整了mysql默认的连接数,调整至500,之前是150

并 发量过高,请求数过多,导致mysql连接数超过了最大连接数的阀值,现在是150,我先把阀值由150加到500

在这里插入图片描述
但是这个不是核心根本问题,于是开始了第二次排查

2. 第2次CPU飙升100%的排查经历(未定位到核心)

异常日志如下图:
在这里插入图片描述
通过查询资料,网上说是连接池的问题,修改对空闲连接池回收的时间,最开始默认为每3分钟回收一次,最后修改为没30分钟回收一次

     <!-- 超时时间;单位为秒。180秒=3分钟 -->
    <property name="removeAbandonedTimeout" value="180" />

在这里插入图片描述

3. 第3次CPU飙升100%的排查经历(SQL语句优化)

这次,服务器整个功能瘫痪了,点击都无法响应,大量请求阻塞,于是又开始了第三次的排次,这次排查以先定位慢查询为思路,然后挑出哪些慢查询SQL语句存在问题,然后对SQL语句进行优化处理。

  • 执行查询计划:
show full processlist; -- 列出全部SQL执行计划

查出有大量的SQL存在慢查询等待,state一直为Sending data
在这里插入图片描述
参数说明:

字段 解释
id ID标识,要kill一个语句的时候很有用
use 当前连接用户
host 显示这个连接从哪个ip的哪个端口上发出
db 链接哪个数据库
command 连接状态,一般是休眠(sleep),查询(query),连接(connect)
time 连接持续时间,单位是秒
state 显示当前sql语句的状态
info 显示这个sql语句

其中state的状态十分关键,下表列出state主要状态和描述,这里只列举常见的几个,详细请百度:

字段 描述
Locked 被其他查询锁住了
Sending data 正在处理SELECT查询的记录,同时正在把结果发送给客户端。
Sleeping 正在等待客户端发送新请求.
  • 挑出具体的SQL语句执行执行计划EXPLAIN:
EXPLAIN SELECT
	f.nick_name AS nickName,
	f.headimgurl AS headImgUrl,
	f.open_id AS openId
FROM
  t_member m
LEFT JOIN t_follow f ON f.open_id = m.open_id WHERE m.green_card = '031011501705982345'
AND f.del_flag = '0'
AND m.del_flag = '0'

t_member 表的索引是普通索引 ,索引名字为member_openid_index, 索引的字段为open_id
t_follow 表的索引是唯一索引 , 索引名字为 唯一, 索引的字段为 open_id
从下面的执行结果图可以看出,t_follow表的唯一索引并没有使用上,而是使用的全表扫描,那么这就找到问题的核心了,一是找出为什么两个表左链接时,其中一张表的唯一索引没有使用上; 二是如何让t_follow表的所有使用上,不去全表扫描。
最后发现,光两张表连接字段加索引还不够,需要在m.green_card上也加一个索引即可,我加了普通索引

在这里插入图片描述
再次执行执行计划,就使用了索引查询:
在这里插入图片描述

  • 执行查询计划:
show full processlist; -- 列出全部SQL执行计划

数据库连接,都是空闲状态,等待客户端发送新的请求,没有产生慢查询的阻塞
在这里插入图片描述

  • 执行计划字段说明:
字段 解释
id 表示查询的类型
select_type 表示选择标识符
table 表示输出结果集的表
partitions 表示匹配的分区
type 表示表的连接类型
possible_keys 表示查询时可能使用的索引
key 表示查询时实际使用的索引
key_len 表示索引字段的长度
ref 表示列与索引的比较
rows 表示扫描出的行数(估算的行数)
filtered 表示按表条件过滤的行百分比
Extra 表示执行情况的描述和说明

四、总结

项目生产环境的数据库一定要建索引,这个非常重要,而且需要根据重要的SQL语句分析索引该不该建、建哪些字段、建什么类型的索引,这些工作不可以忽略,以后我也会注重这个环节;

五、参考

  1. Mysql left join不使用索引问题总结
  2. 查一次left join没有走索引以及原因
发布了329 篇原创文章 · 获赞 232 · 访问量 80万+

猜你喜欢

转载自blog.csdn.net/Thinkingcao/article/details/104650713