访问数据库总超时?这份避坑指南请收好

运维 数据库运维
电商公司大都希望做社交引流,社交公司大都希望做电商,从而将流量变现,所以社交电商一直是热门的创业方向。这个真实的案例来自某家做社交电商的创业公司。

本文转载自微信公众号「数仓宝贝库」,作者李玥   。转载本文请联系数仓宝贝库公众号。

 01事故排查过程

电商公司大都希望做社交引流,社交公司大都希望做电商,从而将流量变现,所以社交电商一直是热门的创业方向。这个真实的案例来自某家做社交电商的创业公司。下面就来一起看下这个典型的数据库超时案例。

从圣诞节平安夜开始,每天晚上固定十点到十一点这个时间段,该公司的系统会瘫痪一个小时左右的时间,过了这个时间段,系统就会自动恢复正常。系统瘫痪时,网页和App都打不开,数据库服务请求超时。

如图1所示,该公司的系统架构是一个非常典型的小型创业公司的微服务架构。

图1 典型的小型创业公司系统架构

该公司将整个系统托管在公有云上,Nginx作为前置网关承接前端的所有请求。后端根据业务,划分了若干个微服务分别进行部署。数据保存在MySQL数据库中,部分数据用Memcached做了前置缓存。数据并没有按照微服务最佳实践的要求,进行严格的划分和隔离,而是为了方便,存放在了一起。

这种存储设计方式,对于一个业务变化极快的创业公司来说是比较合理的。因为它的每个微服务,随时都在随着业务需求的变化而发生改变,如果做了严格的数据隔离,反而不利于应对需求的变化。

开始分析这个案例时,我首先注意到的一个关键现象是,每天晚上十点到十一点这个时间段,是绝大多数内容类App访问量的高峰期。因为这个时间段很多人都会躺在床上玩手机,因此我初步判断,这个故障可能与访问量有关。图2所示的是该系统每天各个时间段的访问量趋势图,正好可以印证我的初步判断。

图2 系统访问量

基于这个判断,排查问题的重点应该放在那些服务于用户访问的功能上,比如,首页、商品列表页、内容推荐等功能。

在访问量达到峰值的时候,请求全部超时。而随着访问量的减少,系统又能自动恢复,因此基本上可以排除后台服务被大量请求冲垮,进程僵死或退出的可能性。因为如果进程出现这种情况,一般是不会自动恢复的。排查问题的重点应该放在MySQL上。

观察图3所示的MySQL服务器各时段的CPU利用率监控图,我们可以发现其中的问题。从监控图上可以看出,故障时段MySQL的CPU利用率一直是100%。这种情况下,MySQL基本上处于不可用的状态,执行所有的SQL都会超时。在MySQL中,这种CPU利用率高的现象,绝大多数情况下都是由慢SQL导致的,所以需要优先排查慢SQL。MySQL和各大云厂商提供的RDS(关系型数据库服务)都能提供慢SQL日志,分析慢SQL日志,是查找造成类似问题的原因最有效的方法。

图3 MySQL服务器各时段的CPU利用率监控图

一般来说,慢SQL日志中,会包含这样一些信息:SQL语句、执行次数、执行时长。通过分析慢SQL查找问题,并没有什么标准的方法,主要还是依靠经验。

首先,我们需要知道的一点是,当数据库非常忙的时候,任何一个SQL的执行都会很慢。所以并不是说,慢SQL日志中记录的这些慢SQL都是有问题的SQL。大部分情况下,导致问题的SQL只是其中的一条或几条,不能简单地依据执行次数和执行时长进行判断。但是,单次执行时间特别长的SQL,仍然是应该重点排查的对象。

通过分析这个系统的慢SQL日志,我首先找到了一条特别慢的SQL。以下代码是这条SQL的完整语句:

  1.  1select fo.FollowId as vid, count(fo.id) as vcounts 
  2.  2 
  3.  3from follow fo, user_info ui 
  4.  4 
  5.  5where fo.userid = ui.userid 
  6.  6 
  7.  7and fo.CreateTime between 
  8.  8 
  9.  9str_to_date(?, '%Y-%m-%d %H:%i:%s'
  10. 10 
  11. 11and str_to_date(?, '%Y-%m-%d %H:%i:%s'
  12. 12 
  13. 13and fo.IsDel = 0 
  14. 14 
  15. 15and ui.UserState = 0 
  16. 16 
  17. 17group by vid 
  18. 18 
  19. 19order by vcounts desc 
  20. 20 
  21. 21limit 0,10 

这条SQL支撑的功能是一个“网红”排行榜,用于排列出“粉丝”数最多的前10名“网红”。

请注意,这种排行榜的查询,一定要做缓存。在上述案例中,排行榜是新上线的功能,由于没有做缓存,导致访问量高峰时间段服务卡死,因此增加缓存应该可以有效解决上述问题。

为排行榜增加缓存后,新版本立即上线。本以为问题就此可以得到解决,结果到了晚高峰时间段,系统仍然出现了各种请求超时,页面打不开的问题。

再次分析慢SQL日志,我发现排行榜的慢SQL不见了,说明缓存生效了。日志中的其他慢SQL,查询次数和查询时长的分布都很均匀,找不到明显有问题的SQL。

于是,我再次查看MySQL服务器各时段的CPU利用率监控图,如图4所示。

图4 系统增加缓存后,MySQL服务器各时段的CPU利用率

把图放大后,我又从中发现了如下两点规律。

1)CPU利用率,以20分钟为周期,非常有规律地进行波动。

2)总体的趋势与访问量正相关。

那么,我们是不是可以猜测一下,如图5所示,MySQL服务器的CPU利用率监控图的波形主要由两个部分构成:参考线以下的部分,是正常处理日常访问请求的部分,它与访问量是正相关的;参考线以上的部分,来自某个以20分钟为周期的定时任务,与访问量关系不大。

图5 系统增加缓存后,MySQL服务器各时段的CPU利用率(附带参考线)

排查整个系统,并未发现有以20分钟为周期的定时任务,继续扩大排查范围,排查周期小于20分钟的定时任务,最后终于定位到了问题所在。

该公司App的首页聚合了大量的内容,比如,精选商品、标题图、排行榜、编辑推荐,等等。这些内容会涉及大量的数据库查询操作。该系统在设计之初,为首页做了一个整体的缓存,缓存的过期时间是10分钟。但是随着需求的不断变化,首页上需要查询的内容越来越多,导致查询首页的全部内容变得越来越慢。

通过检查日志可以发现,刷新一次缓存的时间竟然长达15分钟。缓存是每隔10分钟整点刷新一次,因为10分钟内刷不完,所以下次刷新就推迟到了20分钟之后,这就导致了图5中,参考线以上每20分钟一个周期的规律波形。由于缓存的刷新比较慢,导致很多请求无法命中缓存,因此大量请求只能穿透缓存直接查询数据库。图9-5中参考线以下的部分,包含了很多这类请求占用的CPU利用率。

找到了问题的原因所在,下面就来进行针对性的优化,问题很快就得到了解决。新版本上线之后,再也没有出现过“午夜宕机”的问题。如

图6所示,对比优化前后MySQL服务器的CPU利用率,可以看出,优化的效果非常明显。

图6 优化前后MySQL服务器的CPU利用率对比

02如何避免悲剧重演

至此,导致问题的原因找到了,问题也得到了圆满解决。单从这个案例来看,问题的原因在于,开发人员在编写SQL时,没有考虑数据量和执行时间,缓存的使用也不合理。最终导致在访问高峰期时,MySQL服务器被大量的查询请求卡死,而无法提供服务。

作为系统的开发人员,对于上述问题,我们可以总结出如下两点经验。

第一,在编写SQL的时候,一定要小心谨慎、仔细评估,首先思考如下三个问题。

  • SQL所涉及的表,其数据规模是多少?
  • SQL可能会遍历的数据量是多少?
  • 如何尽量避免写出慢SQL?

第二,能不能利用缓存减少数据库查询的次数?在使用缓存的时候,我们需要特别注意缓存命中率,应尽量避免请求因为命中不了缓存,而直接穿透到数据库上。

不过,我们无法保证,整个团队的所有开发人员以后都不会再犯这类错误。但是,这并不意味着,上述问题就无法避免了,否则大企业的服务系统会因为每天上线大量的BUG而无法正常工作。实际情况是,大企业的系统通常都是比较稳定的,基本上不会出现全站无法访问的问题,这要归功于其优秀的系统架构。优秀的系统架构,可以在一定程度上,减轻故障对系统的影响。

针对这次事故,我在系统架构层面,为该公司提了两条改进的建议。

第一条建议是,上线一个定时监控和杀掉慢SQL的脚本。这个脚本每分钟执行一次,检测在上一分钟内,有没有执行时间超过一分钟(这个阈值可以根据实际情况进行调整)的慢SQL,如果发现,就直接杀掉这个会话。

这样可以有效地避免因为一个慢SQL而拖垮整个数据库的悲剧。即使出现慢SQL,数据库也可以在至多1分钟内自动恢复,从而避免出现数据库长时间不可用的问题。不过,这样做也是有代价的,可能会导致某些功能,之前运行是正常的,在这个脚本上线后却出现了问题。但是,总体来说,这个代价还是值得付出的,同时也可以反过来督促开发人员,使其更加小心谨慎,避免写出慢SQL。

第二条建议是,将首面做成一个简单的静态页面,作为降级方案,首页上只要包含商品搜索栏、大的品类和其他顶级功能模块入口的链接就可以了。在Nginx上实现一个策略,如果请求首页数据超时,则直接返回这个静态页面的首页作为替代。后续即使首页再出现任何故障,也可以暂时降级,用静态首页替代,至少不会影响到用户使用其他功能。

这两条改进建议的实施都是非常容易的,不需要对系统进行很大的改造,而且效果也是立竿见影的。

当然,这个系统的存储架构还有很多可以改进的地方,比如,对数据做适当的隔离,改进缓存置换策略,将数据库升级为主从部署,把非业务请求的数据库查询迁移到单独的从库上,等等,只是这些改进都需要对系统做出比较大的改动升级,需要从长计议之后再在系统后续的迭代过程中逐步实施。

03小结

本文分析了一个由于慢SQL导致网站服务器访问故障的案例。在“破案”的过程中,我分享了一些很有用的经验,这些经验对于大家在工作中遇到类似问题时会有很大的参考作用。下面再来梳理一下这些经验。

1)根据故障时段出现在系统繁忙时这一现象,推断出故障原因与支持用户访问的功能有关。

2)根据系统能在流量峰值过后自动恢复这一现象,排除后台服务被大量请求冲垮的可能性。

3)根据服务器的CPU利用率曲线的规律变化,推断出故障原因可能与定时任务有关。

在故障复盘阶段,我们针对故障问题本身的原因,做了针对性的预防和改进,除此之外,更重要的是,在系统架构层面也进行了改进,整个系统变得更加健壮,不至于因为某个小的失误,就导致出现全站无法访问的问题。

我为该系统提出的第一个建议是定时自动杀死慢SQL,原因是:系统的关键部分要有自我保护机制,以避免因为外部的错误而影响到系统的关键部分。第二个建议是首页降级,原因是:当关键系统出现故障的时候,要有临时的降级方案,以尽量减少故障造成的不良影响。

这些架构上的改进措施,虽然不能完全避免故障,但是可以在很大程度上减小故障的影响范围,减轻故障带来的损失,希望大家能够仔细体会,活学活用。

关于作者:李玥,美团基础技术部高级技术专家,极客时间《后端存储实战课》《消息队列高手课》等专栏作者。曾在当当网、京东零售等公司任职。从事互联网电商行业基础架构领域的架构设计和研发工作多年,曾多次参与双十一和618电商大促。专注于分布式存储、云原生架构下的服务治理、分布式消息和实时计算等技术领域,致力于推进基础架构技术的创新与开源。

本文摘编自《电商存储系统实战:架构设计与海量数据处理》,经出版方授权发布。(ISBN:9787111697411)转载请保留文章出处。

 

责任编辑:武晓燕 来源: 数仓宝贝库
相关推荐

2021-05-13 23:39:19

勒索软件攻击数据泄露

2023-12-13 09:08:26

CPU性能分析Linux

2023-02-10 18:32:21

项目管理实践

2020-07-02 09:55:32

运维架构技术

2023-05-03 20:53:48

2021-02-26 00:46:11

CIO数据决策数字化转型

2020-03-16 10:41:38

服务器开发 Web

2021-09-07 15:41:35

Bug诱因代码

2024-08-09 08:28:14

品牌数据库产品

2024-04-24 13:45:00

2024-04-03 12:30:00

C++开发

2021-02-22 17:00:31

Service Mes微服务开发

2022-03-04 18:11:16

信服云

2023-05-24 10:06:42

多云实践避坑

2021-05-08 12:30:03

Pythonexe代码

2021-05-07 21:53:44

Python 程序pyinstaller

2021-08-02 08:53:26

设计师面试求职

2021-04-28 09:26:25

公有云DTS工具
点赞
收藏

51CTO技术栈公众号