Ruby On Rails 很慢...?
我正在编写一个 Web 应用程序来监控家具厂的生产流程。 它有数千个数据需要处理。 到目前为止,我在 Mongrel + MySQL 上运行 RoR,它真的很慢(某些视图需要 2-4 分钟)。 当我查看 RoR 日志时,数据库查询似乎并不慢(0-10 毫秒)。
RoR将数据库数据转换为对象时是否很慢? 杂种慢吗?
编辑:第一件事:我在开发中。 环境。 在生产环境中,最慢的视图需要 2 分钟(在一台好的计算机上会降低到不到 1 分钟,我的已经有 5 年了)。 凭借 ruby-prof 和一些常识,我发现了哪些方法正在减慢应用程序的速度。 问题是在大型数据集上循环调用单个 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
以下是这些方法的 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}
问题是:我无法真正避免这些单个查询。 我有数千个事件,我必须从中计算复杂的数据。 现在,我在这些方法上使用 memcached,除非您是第一个请求该页面的人,否则这些方法都可以。
I'm writing a web application to monitor a furniture factory production flow. 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). When I look at RoR logs, it seems that database queries aren't slow (0-10ms).
Is RoR slow when it converts database data to object? Is Mongrel slow?
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). With ruby-prof and a bit of common sense, I've found out which methods were slowing down the application. The problem is that single SQL queries are called in loops on larges datasets:
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:
%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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
这是不正常的。 你的某些逻辑会拖慢你的速度。 尝试注释掉您认为需要很长时间的代码片段,看看是否有帮助。 如果确实如此,那么您需要弄清楚如何优化该逻辑。
如果您在迭代大量对象的循环中进行大量计算,那么速度当然会很慢。
这些类型的问题可能出现在任何语言或框架中。 虽然 Ruby 的速度不如其他语言,但大多数时候它已经足够快了。 如果您需要不断地计算大型数据集,那么 Ruby 可能不适合您。 考虑编写一个 Ruby C 扩展来处理性能消耗代码。 但首先尝试诊断和重构。
最后,查看 RubyProf 看看它是否可以帮助您找到瓶颈。
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. If you need to constantly calculate with large data sets then Ruby may not be the right language for you. Look into writing a Ruby C extension that will handle your performance draining code. But first just try to diagnose and refactor.
Lastly, check out RubyProf to see if it can help you find the bottleneck.
前两个答案很有帮助,特别是使用性能监控工具。 我使用 New Relic RPM,它在过去对我帮助很大。
然而,当您尝试将速度从 3 秒加速到 1 秒以下时,这些类型的工具确实是最好的。
在任何正常情况下,2-4分钟的视图渲染绝对是不正常的。
您能否向我们展示一些您的开发日志以找出瓶颈所在?
您是否将浏览器加载图像、JavaScript 或其他文件所需的时间计入此总测量值中?
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.
However, these sorts of tools are really best when you're trying to speed up from, say, 3 seconds to under 1 second.
2-4 minutes for a view to render is absolutely not normal under any normal circumstances.
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?
这么长的执行时间会让我怀疑网络问题 - 也许 DNS 查询在主 DNS 服务器上超时?
Execution times this long would make me suspect a network issue - maybe a DNS query is timing out on a primary DNS server?
您可以尝试使用 JRuby 或切换到 Ruby 1.9。
它们都会带来巨大的性能提升。
JRuby 的问题是使用 C 的 gem 无法编译/工作。 jruby 的“gem”应用程序安装了 Java 等效项,但有些 gem 根本无法工作。
您在 Ruby 1.9 上基本上也会遇到同样的问题。 语法发生了一些变化,但主要问题是大量的 gem 不再起作用。 人们正在更新(请在http://isitruby19.com/查看进度)
You could try to use JRuby or switch to 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. There are Java equivalents which are installed by jruby's "gem" app, but some of the gems simply don't work
You basically will have the same problem with Ruby 1.9. A little bit of syntax changed, but the main problem is that a hugh amount of gems don't work anymore. People are in the progress of updating though (check progress at http://isitruby19.com/)
为什么不预取所有数据并让 for 循环在内存中本地查找它,而不是每次都查询数据库? 对单个视图进行数千次查询表明您的设计存在严重问题。
Why not pre-fetch all the data and have your for loop find it locally in memory, instead of querying the database each time? 1000s of queries for a single view indicates that something is seriously is wrong with your design.
关于这个主题有一些很好的屏幕截图 http://railslab.newrelic.com/scaling-rails
片段缓存和使用 :include (以避免 n+1)之类的东西会有所帮助。 听起来您已经在使用 memcached,那么为什么不卷曲 url 来预取缓存呢?
There are some good screen casts on this topic http://railslab.newrelic.com/scaling-rails
Things like fragmet caching and using :include (to avoid n+1) can help. It sounds like you're already using memcached, so why not curl the url to prefetch the cache?
当我将服务器绑定到盒子的 IP 地址而不是 0.0.0.0 时,这对我来说速度加快了。
When I bound the server to the boxes ip address instead of 0.0.0.0, this sped things up for me.
不过,在执行任何操作之前,您可能会先分析代码,但 for 循环内的查询是导致性能问题的一个非常常见的原因,乍一看这似乎是您的问题。 无论如何,您可能会在此处找到一个实用的分析器:
正如其他答案中已经说过的那样,如果两者都模型是相关的,您应该立即加载关联,这意味着指示 Active Record 执行联接查询:
如果您不需要 ofkbs 而只需要操作,则可以执行内部联接
该解决方案仅执行一个查询,并允许您随后执行迭代已经从数据库收集的数据:
如果查询非常复杂,您应该使用 arel 相反。
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. 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:
If you do not need the ofkbs but only the operations, you could perform an inner join
This solution only preforms one query, and allows you to afterwards iterate through the data that will have already been collected from the DB:
If the queries are very complicated you should use arel instead.
我会同意其他人的观点。 你必须进行简介。 在您知道具体是什么原因导致速度缓慢之前,对代码执行任何操作都是没有意义的。 在不了解原因的情况下尝试解决问题就像感觉生病并决定进行大量手术直到感觉好转一样。 首先诊断您的问题。 它可能是一些小东西,比如网络设置,也可能是代码中的一行坏线。
有关分析的一些提示:
如何分析您的 Rails 应用程序
性能测试 Rails 应用程序
在 Forge - 分析 Rails 应用程序
一旦找到瓶颈,您就可以弄清楚该怎么做。
我推荐这些视频:
Railslab Scaling Rails
现已根据教授结果进行修订:
好的。 现在您可以看到您的问题是您正在使用基于循环另一个活动记录查询结果的查询进行某种计算,我建议您考虑构建一个自定义 SQL 语句,结合您的初始选择标准和循环计算得到你需要的东西。 您绝对可以通过优化 SQL 来加快速度。
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
Performance Testing Rails Applications
At the Forge - Profiling Rails Applications
Once you have found the bottleneck you can figure out what to do.
I recommend these videos:
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. You can definitely speed this up by optimizing the SQL.
每次视图访问会执行多少个 0-10 毫秒的查询? 正在引用数据模型的哪些部分? 您是否使用 :include 来立即加载您的关联?
Rails 有多慢就有多慢。 随着理解速度的加快(通常!)
在上面的基础上进行扩展,您是否有 has_many 关联,特别是您的视图在没有
:include
的情况下引用“多”端? 这会导致主表上的find(:all)
通过连接到详细信息来执行 - 如果您有大量详细记录并且单独处理所有这些记录,这可能会变得昂贵。像这样的事情:
......可能有帮助。 不过,仍在根据稀疏的信息进行猜测。
这里有一个关于该主题的旧 Railscast
How many of those 0-10ms queries are being executed per view access? What parts of your data model are being referenced? Are you using :include to get eager loading on your associations?
Rails is as slow as you make it. 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
? This causes yourfind(: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.Something like this:
...might help. Still guessing from sparse info, though.
There's an old Railscast on the subject here
虽然 RnR 以缓慢着称,但这听起来太极端了是一个简单的语言问题。
您应该运行探查器来准确确定哪些函数速度缓慢以及原因。 减慢 Web 应用程序速度的最常见因素是“n+1 问题"。 也就是说,当数据库中有 n 个数据项时,应用程序会对数据库进行 n 个单独的查询,而不是通过一个查询来获取它们。 但在运行探查器之前您无法知道。 ruby-prof 是我用过的一种分析器。
根据配置文件结果编辑进行编辑:
我坚信您始终可以删除查询循环。 正如 Mike Woodhouse 所说,Rails 的方法是使用 has_many或其他关联,然后让rails自动生成表连接,这是清晰、快速的“Rails方式”。 但是,如果您从纯 SQL 开始,或者关联在这种情况下不起作用,您可以自己生成适当的联接。 如果一切都失败了,您可以创建一个视图或非规范化表来保存以前通过循环找到的结果。 事实上,您必须迭代生成的查询可能表明您的表设计本身存在一些缺陷。
话虽如此,如果缓存查询结果对您来说足够有效,那么就继续使用它。 需要时进行优化。
While R-n-R has a reputation of being slow, this sounds too extreme to be a simple problem with the language.
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". 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. But you can't know until you run the profiler. ruby-prof is one profiler I've used.
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". 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. 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.