他又又又双叒叕踩坑了!这个系统的架构是一个非常典型的小型创业公司的微服务架构。系统的架构如下图:
听了我朋友对问题的描述,我的第一反应是,每天晚上十点到十一点这个时段,是绝大多数内容类 App 的访问量高峰,因为这个时候大家都躺在床上玩儿手机。初步判断,这个故障是和访问量有关系的,看下面这个系统每天的访问量的图,可以印证这个判断。
在访问量峰值的时候,请求全部超时,随着访问量减少,系统能自动恢复,基本可以排除后台服务被大量请求打死的可能性,因为如果进程被打死了,一般是不会自动恢复的。排查问题的重点应该放在 MySQL 上。观察下面这个 MySQL 的 CPU 利用率图,发现问题:
慢 SQL 日志:
首先,你需要知道的一点是,当数据库非常忙的时候,它执行任何一个 SQL 都很慢。所以,并不是说,慢 SQL 日志中记录的这些慢 SQL 都是有问题的 SQL。大部分情况下,导致问题的 SQL 只是其中的一条或者几条。不能简单地依据执行次数和执行时长进行判断,但是,单次执行时间特别长的 SQL,仍然是应该重点排查的对象。这个 SQL 支撑的功能是一个红人排行榜,这个排行榜列出粉丝数最多的 TOP10 红人。
select fo.FollowId as vid, count(fo.id) as vcounts
from follow fo, user_info ui
where fo.userid = ui.userid
and fo.CreateTime between
str_to_date(?, '%Y-%m-%d %H:%i:%s')
and str_to_date(?, '%Y-%m-%d %H:%i:%s')
and fo.IsDel = 0
and ui.UserState = 0
group by vid
order by vcounts desc
limit 0,10
这种排行榜的查询,一定要做缓存。在这个案例中,排行榜是新上线的功能,可能忘记做缓存了,通过增加缓存可以有效地解决问题。
再次分析慢 SQL 日志,排行榜的慢 SQL 不见了,说明缓存生效了。日志中的其他慢 SQL,查询次数和查询时长分布的都很均匀,也没有看出明显写的有问题的 SQL。
把这个图放大后,发现一些规律:
那我们是不是可以猜测一下,对 MySQL 的 CPU 利用率的“贡献”来自两部分:红线以下的部分,是正常处理日常访问请求的部分,它和访问量是正相关的。红线以上的部分,来自某一个以 20 分钟为周期的定时任务,和访问量关系不大。
App 的首页聚合了非常多的内容,像精选商品、标题图、排行榜、编辑推荐等等。这些内容包含了很多的数据库查询。当初设计的时候,给首页做了一个整体的缓存,缓存的过期时间是 10 分钟。但是需求不断变化,首页需要查询的内容越来越多,导致查询首页的全部内容越来越慢。
作为系统的开发人员,对于这次事故,我们可以总结两点经验:
在使用缓存的时候,还需要特别注意的就是缓存命中率,要尽量避免请求命中不了缓存,穿透到数据库上。优秀的系统架构,可以在一定程度上,减轻故障对系统的影响。