简体   繁体   English

Ruby On Rails 很慢...?

[英]Ruby On Rails is slow...?

I'm writing a web application to monitor a furniture factory production flow.我正在编写一个 Web 应用程序来监控家具工厂的生产流程。 It has thousand of data to handle.它有成千上万的数据要处理。 So far, I run RoR on Mongrel + MySQL and it's really really slow (2-4min for some views).到目前为止,我在 Mongrel + MySQL 上运行 RoR,它真的很慢(某些视图需要 2-4 分钟)。 When I look at RoR logs, it seems that database queries aren't slow (0-10ms).当我查看 RoR 日志时,似乎数据库查询并不慢(0-10 毫秒)。

Is RoR slow when it converts database data to object?将数据库数据转换为对象时,RoR 速度慢吗? Is Mongrel slow? Mongrel 慢吗?

Edit : First thing: I was in dev.编辑:第一件事:我在开发。 env.环境In the production environment, the slowest view takes 2min (which would turn down to less than 1min on a good computer, mine is 5 years old).在生产环境中,最慢的视图需要 2 分钟(在一台好的计算机上会减少到不到 1 分钟,我的已经有 5 年了)。 With ruby-prof and a bit of common sense, I've found out which methods were slowing down the application.通过 ruby​​-prof 和一些常识,我发现了哪些方法会减慢应用程序的速度。 The problem is that single SQL queries are called in loops on larges datasets:问题是在大型数据集上循环调用单个 SQL 查询:

ofs = Ofkb.find_by_sql ["..some large SQL query..."]

for of in ofs # About 700-1000 elements
   ops = Operation.find(..the single query..)
   etc.
end

Here are ruby-prof results on those methods:以下是这些方法的 ruby​​-prof 结果:

 %self     total     self     wait    child    calls  name
 32.19     97.91    97.91     0.00     0.00       55  IO#gets (ruby_runtime:0}
 28.31     86.39    86.08     0.00     0.32    32128  Mysql#query (ruby_runtime:0}
  6.14     18.66    18.66     0.00     0.00    12432  IO#write (ruby_runtime:0}
  0.80      2.53     2.42     0.00     0.11    32122  Mysql::Result#each_hash (ruby_runtime:0}

The problem is: I can't really avoid those single queries.问题是:我真的无法避免那些单一的查询。 I've got thousands of events from which I have to compute complex data.我有数千个事件,我必须从中计算复杂的数据。 Right now I'm using memcached on those methods which are OK unless you're the first to request the page.现在我在这些方法上使用 memcached,除非你是第一个请求页面的人。

I'll agree with everyone else.我会同意其他人的意见。 You have to profile.你必须配置文件。 There is no point in doing anything to your code until you know what specifically is causing the slowness.在您知道导致缓慢的具体原因之前,对您的代码做任何事情都是没有意义的。 Trying to fixing a problem without understanding the cause is like feeling ill and deciding to have lots of surgery until you feel better.在不了解原因的情况下尝试解决问题就像感觉不舒服并决定进行大量手术直到感觉好转。 Diagnose your problem first.首先诊断您的问题。 It might be something small like a network setting or it could be one bad line in your code.它可能是网络设置之类的小东西,也可能是代码中的一行错误。

Some tips for profiling:一些分析技巧:

How to Profile Your Rails Application 如何分析您的 Rails 应用程序

Performance Testing Rails Applications性能测试 Rails 应用程序

At the Forge - Profiling Rails Applications在 Forge - 分析 Rails 应用程序

Once you have found the bottleneck you can figure out what to do.一旦找到瓶颈,您就可以弄清楚该怎么做。

I recommend these videos: Railslab Scaling Rails我推荐这些视频: Railslab Scaling Rails

Revised now based on prof results:现在根据教授结果进行修订:

OK.好的。 Now that you can see that your problem is that you are doing some sort of calculation using a query based on looping through the results of another active record query I'd advise you to look into building a custom SQL statement combining your initial selection criteria and the loop calculation to get what you need.现在您可以看到您的问题是您正在使用基于循环访问另一个活动记录查询的结果的查询进行某种计算,我建议您考虑构建一个结合您的初始选择条件的自定义 SQL 语句和循环计算以获得您需要的内容。 You can definitely speed this up by optimizing the SQL.您绝对可以通过优化 SQL 来加快速度。

How many of those 0-10ms queries are being executed per view access?每次视图访问执行了多少个 0-10 毫秒的查询? What parts of your data model are being referenced?引用了数据模型的哪些部分? Are you using :include to get eager loading on your associations?您是否使用 :include 来急切加载您的关联?

Rails is as slow as you make it. Rails 和你做的一样慢。 With understanding comes speed (usually!)理解带来速度(通常!)

Expanding on the above, do you have has_many associations where, in particular, your view is referencing the "many" side without an :include ?扩展上述内容,您是否有 has_many 关联,特别是,您的观点在没有:include情况下引用了“许多”方面? This causes your find(:all) on the master table to be executed with a join to the detail - if you have large numbers of detail records and are processing all of them individually, this can get expensive.这会导致在主表上执行find(:all)并连接到详细信息 - 如果您有大量详细信息记录并单独处理所有这些记录,这可能会变得昂贵。

Something like this:像这样的东西:

Master.find(:all, :include => :details)

...might help. ...可能有帮助。 Still guessing from sparse info, though.不过,仍然是从稀疏信息中猜测。

There's an old Railscast on the subjecthere有一个老Railscast关于这个问题在这里

While RnR has a reputation of being slow , this sounds too extreme to be a simple problem with the language.虽然 RnR以速度慢着称,但这听起来太极端了,不可能是语言的一个简单问题。

You should run a profiler to determine exactly what functions are slow and why.您应该运行分析器来准确确定哪些函数变慢以及为什么变慢。 The most common thing slowing down a web application is the " n+1 problem ".减慢 Web 应用程序最常见的事情是“ n+1 问题”。 That is, when you have n data items in your database, the app makes n separate queries to the database instead of making one query which gets them.也就是说,当您的数据库中有 n 个数据项时,应用程序会对数据库进行 n 个单独的查询,而不是进行一次获取它们的查询。 But you can't know until you run the profiler.但是只有运行分析器才能知道 ruby-prof is one profiler I've used. ruby-prof是我使用过的一种分析器。

Edit based on profile results edit:根据个人资料结果编辑编辑:

I firmly believe that you can always remove a query loop.我坚信您总是可以删除查询循环。 As Mike Woodhouse says, the Rails way to do this is to specify the relations between your tables with a has_many or other association and then let rails automatically generate the table join, this is clear, fast and "the Rails way".正如 Mike Woodhouse 所说,Rails 的方法是用has_many或其他关联指定表之间的关系,然后让 Rails 自动生成表连接,这是清晰、快速和“Rails 方式”。 But if you are starting out with bare SQL or if the associations don't work in this case, you can simply generate the appropriate joins yourself.但是,如果您开始使用裸 SQL 或者如果关联在这种情况下不起作用,您可以简单地自己生成适当的连接。 And If all else fails, you can create a view or denormalized table which holds the results which previously were found through a loop.如果所有其他方法都失败了,您可以创建一个视图或非规范化表,其中包含以前通过循环找到的结果。 Indeed, the fact that you have to iterate through generated queries might be a sign that your table design itself has some flaws.事实上,您必须遍历生成的查询这一事实可能表明您的表设计本身存在一些缺陷。

All that said, if caching your query results works well enough for you, then stay with it.综上所述,如果缓存查询结果对您来说足够好,那么请继续使用它。 Optimize when needed.需要时进行优化。

This is not normal.这不正常。 You have some logic that is slowing you down.你有一些让你慢下来的逻辑。 Trying commenting out bits and pieces of your code that you think are taking a long time and see if that helps.尝试注释掉您认为需要很长时间的代码片段,看看是否有帮助。 If it does then you need to figure out how to optimize that logic.如果是,那么您需要弄清楚如何优化该逻辑。

If you are doing lots of calculations over a loop iterating through a very large number of objects, then of course it will be slow.如果您在遍历大量对象的循环中进行大量计算,那么它当然会很慢。

These types of issues can come up in any language or framework.这些类型的问题可以出现在任何语言或框架中。 While Ruby is not as fast as other languages, it's fast enough most of the time.虽然 Ruby 不如其他语言快,但在大多数情况下它已经足够快了。 If you need to constantly calculate with large data sets then Ruby may not be the right language for you.如果您需要不断地使用大型数据集进行计算,那么 Ruby 可能不是适合您的语言。 Look into writing a Ruby C extension that will handle your performance draining code.考虑编写一个 Ruby C 扩展来处理你的性能消耗代码。 But first just try to diagnose and refactor.但首先只是尝试诊断和重构。

Lastly, check out RubyProf to see if it can help you find the bottleneck.最后,查看RubyProf看看它是否可以帮助您找到瓶颈。

The previous two answers are helpful, especially using performance monitoring tools.前两个答案很有帮助,尤其是使用性能监控工具。 I use New Relic RPM and it's helped me a great deal in the past.我使用New Relic RPM ,它在过去帮助了我很多。

However, these sorts of tools are really best when you're trying to speed up from, say, 3 seconds to under 1 second.但是,当您尝试将速度从 3 秒加速到 1 秒以下时,这些类型的工具确实是最佳选择。

2-4 minutes for a view to render is absolutely not normal under any normal circumstances. 2-4分钟的视图渲染在任何正常情况下都是绝对不正常的。

Could you show us some of your development logs to figure out where the bottlenecks are?您能否向我们展示一些您的开发日志以找出瓶颈所在?

Are you including time the browser takes to load images, javascripts, or other files into this total measurement?您是否将浏览器加载图像、javascript 或其他文件所需的时间包括在此总测量中?

You might profile the code first before doing anything, though, queries inside for loops are a very common cause for performance problems and at first sight this seems your problem.您可能会在做任何事情之前先分析代码,但是,for 循环内的查询是导致性能问题的一个非常常见的原因,乍一看,这似乎是您的问题。 You might anyway find a practical profiler here :无论如何,您可能会在这里找到一个实用的分析器:

As already said on the other answers, if both models are related you should eager load the associations, which implies instructing Active Record to perform join queries:正如在其他答案中已经说过的,如果两个模型都相关,您应该立即加载关联,这意味着指示 Active Record 执行连接查询:

#left outer join
ofkbs=Ofkb.includes(:operation).where(name: "banana")

If you do not need the ofkbs but only the operations, you could perform an inner join如果您不需要 ofkbs 而只需要操作,则可以执行内部联接

#inner join (discards the Ofkbs that do not have any operation)
operations=Operation.joins(:ofkb).where(ofkb:{name:"banana"})

This solution only preforms one query, and allows you to afterwards iterate through the data that will have already been collected from the DB:此解决方案仅执行一个查询,并允许您随后遍历已从数据库收集的数据:

operations=ofkbs.map{|of| of.operations}.flatten

operations.each do |o|
  do_whatever_you_want_with_operation(o)
end

If the queries are very complicated you should use arel instead.如果查询非常复杂,则应改用arel

当我将服务器绑定到 box ip 地址而不是 0.0.0.0 时,这对我来说加快了速度。

这么长的执行时间会让我怀疑网络问题 - 也许 DNS 查询在主 DNS 服务器上超时?

You could try to use JRuby or switch to Ruby 1.9.您可以尝试使用 JRuby 或切换到 Ruby 1.9。
Both of them should result in massive performance boosts.它们都应该导致巨大的性能提升。
The problem with JRuby is that gems that use C won't compile/work. JRuby 的问题是使用 C 的 gem 无法编译/工作。 There are Java equivalents which are installed by jruby's "gem" app, but some of the gems simply don't work jruby 的“gem”应用程序安装了 Java 等价物,但有些 gems 根本不起作用

You basically will have the same problem with Ruby 1.9.你基本上会在 Ruby 1.9 上遇到同样的问题。 A little bit of syntax changed, but the main problem is that a hugh amount of gems don't work anymore.稍微改变了语法,但主要问题是大量 gem 不再起作用。 People are in the progress of updating though (check progress at http://isitruby19.com/ )人们正在更新中(在http://isitruby19.com/查看进度)

Why not pre-fetch all the data and have your for loop find it locally in memory, instead of querying the database each time?为什么不预取所有数据并让您的 for 循环在内存中本地查找它,而不是每次都查询数据库? 1000s of queries for a single view indicates that something is seriously is wrong with your design.对单个视图的 1000 次查询表明您的设计存在严重问题。

There are some good screen casts on this topic http://railslab.newrelic.com/scaling-rails关于这个主题有一些很好的屏幕投射http://railslab.newrelic.com/scaling-rails

Things like fragmet caching and using :include (to avoid n+1) can help.诸如片段缓存和使用 :include (避免 n+1)之类的事情会有所帮助。 It sounds like you're already using memcached, so why not curl the url to prefetch the cache?听起来您已经在使用 memcached,那么为什么不卷曲 url 以预取缓存呢?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM