【问题标题】:How to tune a Ruby on Rails application running on Heroku which uses production level Heroku Postgres?如何调整在使用生产级 Heroku Postgres 的 Heroku 上运行的 Ruby on Rails 应用程序?
【发布时间】:2014-04-30 02:12:03
【问题描述】:

我工作的公司决定将他们的整个堆栈转移到 Heroku。主要动机是它的易用性:没有系统管理员,没有哭泣。不过我还是有一些疑问...

我正在对应用程序平台和 Postgres 服务进行一些负载和压力测试。我使用blitz 作为 Heroku 的插件。我攻击了用户数量在 1 到 250 之间的网站。我得到了一些非常有趣的结果,我需要帮助来评估它们。

测试堆栈:

应用规范

它并没有什么特别之处。

  • Rails 4.0.4
  • 独角兽
  • database.yml 设置为连接到 Heroku postgres。
  • 不使用缓存。

数据库

这是一个Standard Tengu(Heroku 的命名约定总有一天会害死我的 :) 正确连接到应用程序。

Heroku 配置

按照“Deploying Rails Applications With Unicorn”文章中的说明,我在unicorn.rb 上应用了所有内容。我有 2 个常规网络测功机。

WEB_CONCURRENCY  : 2
DB_POOL          : 5

数据

  • episodes 桌数100.000~
  • episode_urls 表数 300.000~
  • episode_images 桌数 75.000~

代码

episodes_controller.rb

  def index
    @episodes = Episode.joins(:program).where(programs: {channel_id: 1}).limit(100).includes(:episode_image, :episode_urls)
  end

episodes/index.html.erb

<% @episodes.each do |t| %>
<% if !t.episode_image.blank? %>
<li><%= image_tag(t.episode_image.image(:thumb)) %></li>
<% end %>
<li><%= t.episode_urls.first.mas_path if !t.episode_urls.first.blank?%></li>
<li><%= t.title %></li>
<% end %>

场景 #1:

Web dynos   : 2
Duration    : 30 seconds
Timeout     : 8000 ms
Start users : 10
End users   : 10

结果:

HITS 100.00% (484)
ERRORS 0.00% (0)
TIMEOUTS 0.00% (0)

这次冲刺在 30.00 秒内产生了 218 次成功命中,我们 将 6.04 MB 的数据传入和传出您的应用程序。平均命中 7.27/秒的速率转化为大约 627,840 次点击/天。

场景 #2:

Web dynos   : 2
Duration    : 30 seconds
Timeout     : 8000 ms
Start users : 20
End users   : 20

结果:

HITS 100.00% (484)
ERRORS 0.00% (0)
TIMEOUTS 0.00% (0)

这次冲刺在 30.00 秒内产生了 365 次成功命中,我们 将 10.12 MB 的数据传入和传出您的应用程序。平均命中 12.17/秒的速率转化为大约 1,051,200 次点击/天。这 平均响应时间为 622 毫秒。

场景#3:

Web dynos   : 2
Duration    : 30 seconds
Timeout     : 8000 ms
Start users : 50
End users   : 50

结果:

HITS 100.00% (484)
ERRORS 0.00% (0)
TIMEOUTS 0.00% (0)

这次冲刺在 30.00 秒内产生了 371 次成功命中,我们 将 10.29 MB 的数据传入和传出您的应用程序。平均命中 12.37/秒的速率转化为大约 1,068,480 次点击/天。这 平均响应时间为 2,631 毫秒。

场景 #4:

Web dynos   : 4
Duration    : 30 seconds
Timeout     : 8000 ms
Start users : 50
End users   : 50

结果:

HITS 100.00% (484)
ERRORS 0.00% (0)
TIMEOUTS 0.00% (0)

这次冲刺在 30.00 秒内产生了 484 次成功命中,我们 将 13.43 MB 的数据传入和传出您的应用程序。平均命中 16.13/秒的速率转化为大约 1,393,920 次点击/天。这 平均响应时间为 1,856 毫秒。

场景#5:

Web dynos   : 4
Duration    : 30 seconds
Timeout     : 8000 ms
Start users : 150
End users   : 150

结果:

HITS 71.22% (386)
ERRORS 0.00% (0)
TIMEOUTS 28.78% (156)

这次冲刺在 30.00 秒内产生了 386 次成功命中,我们 将 10.76 MB 的数据传入和传出您的应用程序。平均命中 12.87/秒的速率转化为大约 1,111,680 次点击/天。这 平均响应时间为 5,446 毫秒。

场景 #6:

Web dynos   : 10
Duration    : 30 seconds
Timeout     : 8000 ms
Start users : 150
End users   : 150

结果:

HITS 73.79% (428)
ERRORS 0.17% (1)
TIMEOUTS 26.03% (151)

这次冲刺在 30.00 秒内产生了 428 次成功命中,我们 将 11.92 MB 的数据传入和传出您的应用程序。平均命中 14.27/秒的速率转化为大约 1,232,640 次点击/天。这 平均响应时间为 4,793 毫秒。你有更大的问题, 但是:26.21% 的用户在这个高峰期间经历了超时或 错误!

一般总结:

  • 即使有 150 个用户向应用程序发送请求,“命中率”也不会超过 15 个。
  • 越来越多的网络测功机无助于处理请求。

问题:

  1. 当我使用缓存和 memcached(Heroku 的 Memcachier 插件)时,即使是 2 个网络测功机也可以每秒处理 >180 次点击。我只是想了解 dynos 和 postgres 服务在没有缓存的情况下可以做什么。通过这种方式,我试图了解如何调整它们。怎么办?

  2. 据说标准 Tengu 有 200 个并发连接。那么为什么它从来没有达到这个数字呢?

  3. 如果拥有生产级别的数据库和增加 Web dynos 无助于扩展我的应用程序,那么使用 Heroku 有什么意义?

  4. 可能是最重要的问题:我做错了什么? :)

感谢您阅读这个疯狂的问题!

【问题讨论】:

  • 大多数常用的 PostgreSQL 调优和诊断工具,如 pg_stat_statementspg_stat_plansauto_explainpgbadger 等都不适用于 Heroku。即使你愿意,你也没有太多可以在 Heroku 上调音的东西。也就是说:您如何确定数据库永远不会达到 200 个连接?另外,你为什么要它,除非它有 200 个 CPU 和一个高度并行的 I/O 子系统?
  • 我在 sql 控制台中使用SELECT COUNT(*) from pg_stat_activity; 命令,因此我可以看到活动连接。这就是我确定连接数的方式。当有 150 个请求进入我的应用程序时,我不应该假设有超过 15 个连接吗?
  • 不,不一定。除非数据库服务器 非常 大,否则最好使用更少的连接来更快地处理请求。见wiki.postgresql.org/wiki/Number_Of_Database_Connections
  • pg_stat_statements 在 9.2+ 上默认可用并包含在内。

标签: ruby-on-rails postgresql heroku stress-testing


【解决方案1】:

我特别想通了这个问题。

首先,记住我在视图中的代码:

<% @episodes.each do |t| %>
<% if !t.episode_image.blank? %>
<li><%= image_tag(t.episode_image.image(:thumb)) %></li>
<% end %>
<li><%= t.episode_urls.first.mas_path if !t.episode_urls.first.blank?%></li>
<li><%= t.title %></li>
<% end %>

在这里,我在我的迭代中获取每一集 episode_image。尽管我一直在控制器中使用includes,但我的表模式存在一个大错误。 我的episode_images 表中没有episode_id 的索引!。这导致查询时间非常长。我使用 New Relic 的数据库报告找到了它。所有其他查询时间为 0.5 毫秒或 2-3 毫秒,但 episode.episode_image 导致将近 6500 毫秒!

我不太了解查询时间和应用程序执行之间的关系,但是当我在episode_images 表中添加索引时,现在我可以清楚地看到差异。如果您的数据库架构正确,您可能不会遇到通过 Heroku 进行扩展的任何问题。但是任何测功机都无法帮助您处理设计不佳的数据库。

对于可能遇到同样问题的人,我想告诉你一些我对 Heroku web dynos、Unicorn workers 和 Postgresql 活动连接之间关系的发现:

基本上,Heroku 为您提供了一个 dyno,它是一种具有 1 个核心和 512MB 内存的小型虚拟机。在那个小虚拟机中,您的 Unicorn 服务器运行。 Unicorn 有主进程和工作进程。您的每个 Unicorn 工作人员都有自己与现有 Postgresql 服务器的永久连接(不要忘记查看this)这基本上意味着当您有一个 Heroku dyno 并在其上运行 3 个 Unicorn 工作人员时,您至少有4 个活动连接。如果您有 2 个网络测功机,则您至少有 8 个活动连接。

假设您有一个具有 200 个并发连接限制的标准 Tengu Postgres。如果您的查询有问题且 db 设计不佳,则 db 或更多 dynos 都无法在没有缓存的情况下为您节省...如果您有长时间运行的查询,我认为除了缓存之外别无选择。

以上都是我自己的发现,如果有什么不对的地方请各位大侠指教。

【讨论】:

  • 感谢您对您的解决方案发表评论。我的第一个问题是将您在 heroku 上看到的性能与本地性能进行比较,这将揭示相同的(相对)性能,并突出显示缺失的索引。例如。为什么 Heroku 上的性能与我的本地测试有很大不同?
  • 事实上,您最好将此问题发布为“问题”,这样其他人就可以轻松获得答案。创建它们后,请告诉我链接,好吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-11-15
  • 1970-01-01
  • 2014-11-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-17
相关资源
最近更新 更多