复盘一次小公司遇突发流量的扑火抢救

2,060 阅读7分钟

今天双十一,木有节目!!!静静写个文章吧 :-D

2018 年元旦前夕,公司业务系统流量突然增加 5~6 倍,然而并无相关预演或准备,于是乎系统瘫痪了,钱也不再进账了,对于小公司来说,痛并快乐着!

经过此次扑火抢救,个人总结了一些小经验,大厂用不上,但对总共就几个人的小微公司而言可能有些帮助。

PHP 技术栈,然而道理是相通的!不必纠结世界上最好的语言是哪个 :)

简略版

  • 升级至 PHP7
  • 启用 OPcache
  • 借日志识别隐患和瓶颈
  • 清理数据库慢查询
  • 关闭 Debug 及 Log
  • 增加缓存时间

补充版

  • 还没升级到 PHP7 的,如果可以,建议升级。个人认为,但凡有点技术追求的都不会死死抱着 N 年的东西不愿意放手吧,万年 CentOS 6.x 甚至 5.x 式的“稳定”就“真稳定”?技术债可以欠,但不应该欠太多。造轮子并非多数小公司的强项,老旧的技术栈维护起来真让人难受。

并非有意针对CentOS。本科时折腾过CentOS、Fedora、openSUSE、Mandriva Linux、Elementary OS、Debian、Ubuntu, Ultimate Edition、红旗 Linux、Ylmf OS(雨林木风OS,现名StartOS)、Linux Deepin(深度OS,并非平时电脑城装机用的Ghost版!)……各种我能接触到的 Linux 发行版,最终还是认为 Ubuntu 更友好,适合我这样的小白用户,毕业后遇到了 Linux Mint,至今已用 5 年+。前年省吃俭用换了一台i7+GTX1070+64G+1TSSD+4K屏幕的笔记本,拿到手第一件事情就是格盘装上 Linux Mint。上一台笔记本i7+16G+SSD还是一样的系统,毕业第二年用虚拟机搞 iOS 开发,感觉比低配的 Mac mini 还流畅些,不过流畅度当然比不上公司配的 MacBook Pro。

  • 启用 OPcache,鸟哥博客文章 《让PHP7达到最高性能的几个Tips》 第一条。

  • 日志建议重点关注两点:

    • php-fpm 的 slow log (如代码中是否出现 sleep 一类的函数?实在有必要吗?)
    • 数据库的慢查询日志 (索引、触发器、存储过程等可能造成慢查询甚至锁表)
  • 清理数据库慢查询

个人认为,就一般互联网小公司而言,系统瓶颈一般先出现在数据库。可能平时业务量小,硬件资源充足,问题不显现,估计都不太重视这问题。数据库连接池的问题并不是瓶颈,面对大量的慢查询再多的连接池也不管用啊,先把慢查询处掉就好了,查询慢甚至锁表可能只是个索引的问题。。。

  • 关闭 Debug 这个没啥好说的,但就怕忘记了!!!至于 Log 嘛,看情况吧,要注意非异步的、可能引起阻塞的 Log (比如说直接在每一个 request 里同步写日志就不合适)

太长不看版 (复盘)

2018 年元旦前夕,系统流量瞬间猛增 5~6 倍,可近期并无广告推广投入啊,当时第一反应是遭到攻击了,但冷静一想,哪个闲着没事盯上个小公司,多大仇啊。查看各种数据推断此次为自然流量。

由于无相关演练和应急预案,系统瘫痪了,公司收入也基本停了,痛并快乐着!

我当时首先建议升级 PHP。将其中一台服务器升级后发现内存和CPU消耗减半 (注:其实并未真正解决问题)。

接着其它服务器也升级了,可尴尬的是,没过多就,系统又瘫痪了,而且多了很多错误日志。我司用的PHP框架是 ThinkPHP 3.x,叠加历史遗留问题,根本不可能平滑升级。于是乎降级回到 5.6 的版本。

回到原点,而且问题没有解决!!!

于是乎,下一个尝试是,增加服务器数量、提高数据库连接数上限(阿里云的数据库)。然而都加倍了,压力还是没有缓解!没有缓解!换句话说,关键不在此,再加就是花冤枉钱。

接着我拿到了一台服务器的 SSH 账户。

注:我个人负责的工作相对杂乱,从 Android 到 iOS,到 Java web 再到 PHP web……,但却没管过公司服务器的维护工作,当然我个人负责的外包项目的话就是从服务器选购、系统环境配置到项目开发、部署、维护……,从 0 到 1 全包了。

我首先启用Opcache,然后调整了 nginx 和 php-fpm 的一些参数,起到了一定的作用,但并未解决问题。

然后我顺手把 PHP 的慢执行日志 slow log 打开了。由于系统堵得厉害,一大堆的 log,没能抓到关键,当天无果。

推荐个好用的工具 mosh,关键时候不掉线哦

毕竟用户也是要休息的,慢慢流量下去了,系统恢复正常。不过晚上也没梦到解决方案 :-D

第二天,继续排查,各种调参无果。中午吃过饭后,妈给我来了个电话,问我是不是在加班。这。。。

挂电话后,我犀利的眼睛注意到了一条不起眼的日志,顺着日志指示的行数,我翻看了一下代码,发现该行调用了个 usleep 函数,5 秒钟,我的天,没必要吧,于是乎我直接把它注释掉,重启 php-fpm,奇迹出现了,该台服务器瞬间畅通!接着排查了所有代码中出现的 usleep 函数,统统注释掉,重启 php-fpm。

系统恢复正常服务,钱也开始进来了。。。

然而故事到了这里还没结束!

下午流量又逐渐增加,到傍晚饭点的时候,系统又响应缓慢了,但当时我在外吃饭,没带电脑,一时也赶不回去,没办法,我只好掏出手机试试了。还好下午有先见之明,准备了个批量重启服务器 php-fpm 的工具,一重启它就恢复正常了,过段时间堵了就再重启,本来是有效的,可流量还在增加,慢慢地就不管用了,不过这时我已经吃完饭回到家。马上电脑打开,继续奋战!

再推荐个小工具 PSSH (Parallel SSH)

最后同事定位到的原因是,另一个子系统的数据库的 session 数据表没添加索引,导致 session 过期的时批量删除的时间过长,估计是锁表了。

主系统还可能出问题的地方我都检查过了(其实我是不清楚还有个老系统在跑☺),而且 session 的驱动也从 mysql 换到了 redis,没想到的是拖后腿的是个子系统,一款已上线多年的App (此次流量全是它引来的),把数据库拖垮了。

session 表添加索引后,系统又恢复正常了。

回顾整个扑火过程,关键点有两个,一是 usleep 函数,二是数据库索引,启用 OPcache 是有些作用的,而关闭数据库触发器啥的也起到了不小的作用,而增加接口缓存时间的操作其实治标不治本,没有从根本上解决问题,而且这次连表面上解决问题的作用也没起到。

此次扑救的突破口在于日志,一是 php-fpm 的慢执行日志,二是 mysql 的慢查询日志。通过排查导致 php-fpm 进程排长队的原因就能顺藤摸瓜解决问题。

在服务器资源一定的情况下,php-fpm 的进程数同样有限,如果出现进程休眠(例如 usleep )或数据库连接数达上限(慢查询导致)的话,服务器资源充足的情况下没问题,但 php-fpm 的进程一旦排长队,接下来很可能就是雪崩效应,系统就瘫痪了。

以上是本人的一点经验总结,可能存在不少错误和不足,欢迎各位小伙伴来指正和讨论,谢谢!

本人普通 985 工科院校,工商管理类专业,双学位。创业小公司工作 5 年+,全菜工程师,想换个离家近的地方工作,近期在寻求珠三角地区的新坑,偏后端方向,求介绍啊(已换坑)!