Rails Rails 性能优化 - 优化之前

lanzhiheng · 2022年03月02日 · 最后由 lanzhiheng 回复于 2022年03月11日 · 1169 次阅读

这篇文章简单归纳了笔者对于优化的一些想法,并推荐了两个自己常用的监控工具,希望对刚入行的大伙能有些帮助。原文连接 https://lanzhiheng.com/posts/rails-performance-before-action


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

前言

不瞒大家说,第一次遇到性能瓶颈,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% 左右。

总结

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

Hey,man~ Good job!👊

👍 这几天了解了一点玉的知识

欢迎来我司交流性能问题,最近我们也在优化😀

lyfi2003 回复

😂 好啊。等这波疫情过去先吧。我们一直都想去你们那边走走。太久没见了。

redis 撑起了我的各种奇葩业务

mark 高质量

谢谢分享,最近也打算优化性能

关于性能优化可以看一下 https://nateberk.gumroad.com/l/apocrypha。 优化的核心思路是

  1. Observation (Noticing behavior in production or development)
  2. Hypothesis (Profiling to figure out what's causing the behavior)
  3. Test (Benchmarking)
  4. Analyze (Observing new production behavior)

书上的内容虽然作者的博客上大部分都有,不过现在才 10 美元,书也是这两年刚发得,内容没有过时,看看也能有不少收获。

piecehealth 回复

我去?才 10 美元吗? https://nateberk.gumroad.com/l/rails-perf-starter 我前不久刚买。看了一丢丢。为啥我是 50 刀 。。😂

lanzhiheng 回复

你俩说的不是一本, 他说那本是以前出的. 话说你这本感觉咋样?

FinnG 回复

只看了一点点,不过我个人觉得挺好的。 一开始就提到类似 2/8 法则,对于线上系统来说,监控瓶颈更优于 Benchmark。等看完再写个书评吧。 3-4 百页,英文也不是很好,估计没那么快。

这不就是 Rails 理念之一嘛,80% 的慢估计都是 IO 相关的问题。

公司内部 java 系统,乱用多线程做 http 请求,io 阻塞,策略极端,单从堆栈看不出问题,只能看到线程越来越多,服务不可用,但是没有死,读写文件,目录中文件过多也会有这样的问题。

1,大部分业务都撑不到需要优化的时候。 2,真的撑到了也不必厮守 Rails,这时候业务可能固化下来了,用性能更好的语言实现也不赖。

pynix 回复

其实我是认同的。只不过现在暂时没找到换其他语言的理由。我们这种小公司,目前其实灵活性反而比较重要了。换个 Java 或者 Go 一个人搞不过来啊。

lanzhiheng 回复

那就说明业务还是没有固化下来。

pynix 回复

估计固化下来也不会说换语言重写吧。可能一些特殊的业务做成微服务用别的语言写倒是有可能。

lanzhiheng 回复

固化下来的用高性能语言写很划算的。

感谢分享。恰好最近也做了类似的工作,有一些浅见。

个人觉得性能优化的整个过程恰好就是「科学方法」的应用过程 —— 从观测开始,描述问题,做出假设,构建实验来验证假设,观察实验结果,提出新的问题,进一步迭代下去,直到形成了一个自洽的理论。

在性能优化这个场景,「科学方法」的过程可以被具体化为:

  1. 观察并表述性能问题。
  2. 收集程序运行信息(称为 Performance Analysis 或 Profiling)。
  3. 根据收集到的信息提出假设。
  4. 验证假设。
  5. 给出优化方案,重新观察性能问题。

应用到实际生产中,可能会是这样:

  1. 用户或开发者觉得应用的反应有些慢。
  2. 使用性能分析工具收集数据。(这些性能分析工具可以是 OP 提到的 NewRelic、RDS 监控,还可以是火焰图等等)
  3. 开发者根据这些信息做出假设。
  4. 构建最小环境,做 benchmark 来验证假设。
  5. 做出优化,重新观察。不停迭代。

有了正确的方法 + 给够时间,大部分问题都能解决~

pynix 回复

哦?“划算” 怎么说?

lanzhiheng 回复

成本划算。

pynix 回复

等有合适的场景的时候我试试。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号