随着自家应用访问量越来越大,很多性能问题会渐渐暴露出来。系统跑得慢,没什么可耻的,我相信大多数服务端开发者都有过这种经历。

前言

不瞒大家说,第一次遇到性能瓶颈,CPU/内存吃满的时候,笔者只感受到深深的无助。因为完全不知道问题出在哪里,公司的投诉声音也很大,老板估计也受不了了,叫我不要吝惜钱,该扩容就扩容。然而直觉告诉我,应用会有很大的优化空间(因为代码写得烂),远远没到扩容的时候。

优化之前,小心理想主义的优化

许多小伙伴想到要优化应用,基本都会

  1. 把那些复杂的逻辑,计算量大的工作用别的语言重写。
  2. 找更先进的算法替代掉原来老的算法,比如用快速排序替换掉冒泡排序。
  3. 网上寻找一般高手是怎么优化Ruby的,跟着高手一步步做。

或许对于有洁癖的程序员来说这些做法会让他们舒服一点,而且会有一种“我认真在做优化”的满足感。然而笔者以为,在不知道瓶颈在哪的情况下,盲目地去做优化,其实是理想主义的优化,对于现实而言或许有效,但效果往往不明显。

别种语言重写会更快?

首先,Ruby或许在微观上,并没有Java那么快。然而在实际业务场景中,往往不是瓶颈所在。不信我可以给你看看证据

Screen Shot 2022-03-01 at 9.17.44 AM.png

可见,现在一个请求耗费的时间大概是200ms ~ 500ms左右。其中Ruby运行所占的比重大概是80ms~100ms左右。使用别种语言去重写能优化到什么地步?80ms可以到40ms,30ms或者20ms吗?或许可以,也或许不行,在网络环境稍微糟糕一点的情况下,这种提升用户几乎也察觉不出来。可能对于执着的程序员来说哪怕快几ms也是有意义的,然而重写带来的这几ms的提升可能是几个人一个月的工作量,对创业公司来说得不偿失。在这方面我们不追求极速,足够快就好。

算法是否先进,可能并不是瓶颈的所在

我就拿排序算法来做个比喻,大家学习算法课程的时候,肯定有见过类似这种图,表示数据量到达一定量的时候,冒泡排序有多无力。

comparison.png

不过仔细一看可以发现,在数据量并不是很大的时候,各个排序之间的性能区别,其实并没有多大。而在正常的业务场景中,数据量稍微大一点的排序,可能都是靠数据库的排序引擎来帮忙实现,优化空间并不大。而真正需要自己编写排序的,往往数据量不会很大。比方说参数签名的时候,需要对参数进行排序,并组合成查询字符串。

params.to_a.sort.map { |a, b| "#{a}=#{b}" }.join('&')

类似这种排序,数组的数据量最多也不超过10个。或许你可以找到比Ruby内置排序更优的排序算法来优化这个排序过程,不过可能对整个系统来说,这种优化的效果不会说特别明显?

高手的做法

高手的做法有很大的参考价值,不过他们的文章都是根据一般会影响性能的场景做的简单总结,不一定能够解决燃眉之急。毕竟每个系统的业务逻辑都不一样,你的系统的性能瓶颈可能在于N+1查询太多了,而他的系统可能是有那么几个慢查询把整个系统都给耽误了。这个时候如果脱离实际场景,盲目地按照高手的建议去做优化,可能系统性能会取得一定的提升,但在真正瓶颈的影响下,这种提升很难察觉的出来。

优化之前,先找出性能瓶颈

一般来说,随着用户量的增长,系统会渐渐慢下来。不知道的还以为是硬件资源不足,容易脑袋一热就给各种云服务商充值。殊不知可能优化掉一条代码,或者SQL语句,一年下来就给公司省下好几千大洋。笔者的系统就经历过

  1. 一个30多秒的慢查询,耽误了整个系统(慢查询日志可以找出来)。
  2. 在频繁请求的接口中N+1查询太多,导致数据库服务的CPU总是处于吃满的状态。
  3. 一些早期编写的字段函数(已经没啥用了,却忘了删除),在用户增长之后由于每次的查询量都太大,这些废弃代码渐渐成了性能瓶颈。

针对这些问题,其实都需要系统在生产环境运行,用户渐渐增多才会慢慢暴露出来,单从代码层面去分析,真的很难把他们一一找出来。其实性能优化也遵循二八法则,百分之20的性能瓶颈,其实就会浪费掉系统百分之八十的资源。引入监控大有必要,这里就推荐两个笔者现在正在使用,觉得还不错的工具

a. Newrelic

要监控整个Rails应用的情况,可以借助第三方服务Newrelic。只需要很简单的几步就能把Newrelic集成到你的Rails应用中去。Newrelic提供了各式各样的数据面板,可以比较直观地了解到

  1. 每个请求的请求量,响应时间,以及系统最耗时的请求是哪些。
  2. 访问量有多少。
  3. 系统的架构信息。
  4. 系统的错误率是多少。
  5. 请求的底层调用细节。能够很直观地观察到,导致某个请求性能不尽如人意的地方在哪。或许是这个请求里面N+1查询太多,又或者是某个慢查询影响了整个请求的响应速度。
  6. ....

还有很多其他有价值的数据,无法一一列举。这里提供一些相关的截图数据

首页汇总

Screen Shot 2022-03-01 at 9.16.58 AM.png

系统架构信息

Screen Shot 2022-03-01 at 9.26.29 AM.png

数据库查询面板

Screen Shot 2022-03-01 at 9.27.08 AM.png

请求面板

Screen Shot 2022-03-01 at 9.23.02 AM.png

请求细节

Screen Shot 2022-03-01 at 9.27.45 AM.png

Newrelic的监控功能还是比较多的,这里就只展示个人用得比较多的面板。个人比较喜欢它能够较为精准且迅速地帮我找到一些性能瓶颈,特别是服务器开始慢的时候,一上去基本上就能看出问题主要出自哪里,让我可以迅速修复问题。不过这么好用的服务并不是免费的,根据存储量的不同会收取一定的费用,国外好用的服务想白嫖有点难,大家根据自己的实际情况进行选择就好,不过每个月还是有100G存储空间是免费的。

b. RDS慢查询日志

笔者强烈建议在生产环境中,只要预算不是太有限都直接上云厂商的RDS系统。他们可以提供独立的主机来运行你需要的数据库服务,配套完善的监控以及备份功能。以下是阿里云RDS服务的监控面板

性能面板

Screen Shot 2022-03-01 at 9.31.10 AM.png

慢查询面板

Screen Shot 2022-03-01 at 9.32.28 AM.png

信息还算比较齐全,目前笔者看的比较多的还是慢查询面板。查询时间超过多少被定义为慢查询可以自己设定,笔者设定是200ms。有些数据库中比较慢的查询可能会占用掉系统很大一部分的CPU跟内存。把这部分的性能瓶颈优化掉之后,系统的整体表现会提升不少。笔者做了几轮优化之后RDS的CPU使用率就从80-90%的高居不下,下降到现在的12-30%左右。

总结

这篇文章简单归纳了笔者对于优化的一些想法,并推荐了两个自己常用的监控工具,希望对刚入行的大伙能有些帮助。