Rails 常见性能问题一览[转载]

Rails 常见性能问题一览

作者 Stefan Kaes译者 Jason Lai 发布于 2007年3月26日 上午6时48分

社区
Ruby
主题
Web框架,
性能和扩展性,
Ruby on Rails

在最近几个月里,我从性能问题的角度,分析了不少 Rails 应用程序(里面有一些牵涉到我的咨询业务,另外一些则是开源应用)。这些应用程序面向的多个领域之间存在着诸多差异,导致每项性能调优任务都颇具挑战性。然而,它们之间还是存在不少共性,使得我们得以提炼出不少出现问题的地方,正是它们导致了很多应用程序难以达到高性能。它们包括:

  • 选用了缓慢的 Session 容器
  • 本可在启动时一次性完成的操作被放在每次请求中执行
  • 请求处理过程中重复相同运算
  • 频繁从数据库加载过多数据(尤其在使用关联进行连接的情况下)
  • 过分依赖于低效 helper 方法

除此之外,Rails 框架自身依然存在某些问题区(problem areas),它们的性能是我所希望在今后改善的。里面有一部分可以在应用程序级别解决,另外一部分则无能为力。下面是我比较感兴趣的问题:

  • Route 识别和 Route生成
  • ActiveRecord 对象的构造
  • SQL 查询的构造

我会在下文中罗列出针对以上问题的部分编码技巧。

一些忠告

遵循本文的建议,有可能改善你的应用程序的性能,但也可能行不通。性能调优是件费脑筋的事情,尤其在实现语言结构的性能特征没有被明确规范的时候,更是如此(在 Ruby 里面就是这种情况)。

我强烈建议,在进行任何变更之前,先评测一下你的应用程序原先的性能,之后每次变更后再次进行评测。我编写了一个包,叫做 railsbench,这是一个很不错的性能回归测试工具。它简单易用,只需几分钟你就可以得到性能的基准数据。不过比较遗憾,它无法告诉你,在你的应用程序里时间到底花费在哪里(译注:此外,你的应用程序所调用的 Rails 框架的代码也是有时间开销的)。

假如你手头有台运行 Windows 的机器(或者一台带双启动的 Intel Mac),我建议你去试试 Software Verification Ltd.(SVL)的 Ruby Performance Validator(RPVL)。对于我深及 Rails 框架内核的 Rails 性能调优工作,特别是在我所建议的在已有热点视图基础上的调用图功能被 SVL 实现之后,我发现它真是功勋卓著。据我所知,它是目前市面上唯一的 Ruby 应用程序性能分析工具。Railsbench 内建了对 RPVL 的支持,这样也使得在 RPVL 之下运行被 Railsbench 定义的基准变得易如反掌。

选用 Session 容器

Rails 提供了几个内建的 Session 容器。在所有我分析过的应用程序里,要么使用了将 Session 信息储存在你文件系统上独立文件的 PStore,要么用了和数据库打交道的 ActiveRecordStore。这两个方案都不甚理想,特别是拖累了缓存 Action 的页面(action cached pages)。这里提供两个好用得多的备选方案供大家参考:SQLSessionStore 和 MemCacheStore。

SQLSessionStore 通过以下手段避免了 ActiveRecordStore 相关的额外性能开支:

  • 避免使用事务(对于 SQLSessionStore 的正确操作,它们并不是必要的)
  • 撤销向数据库更新“created_at”和“updated_at”的操作

如果使用 MySQL,你应当保证使用 MyISAM 表来存储 Session 数据,因为它要比 InnoDB 表快很多,并且它不会强制你使用事务处理。前不久我又为 SQLSessionStore 增加了 Postgres 支持,不过,用于 Session 存储 Postgres 看起来要比 MySQL 慢得多。因此,如果你打算使用基于数据库的 Session 存储,我推荐为 Session 表安装 MySQL 数据库(我想不出一个基于 session id 的需要连接的更好用例(use case))。

MemCacheStore 要比 SQLSessionStore快得更多。我的测评结果显示,对于缓存了 Action 的页面它能够带来了 30% 的速度提升 。你得先安装 Eric Hodel 的 memcache client ,并在 environment.rb 中做相应配置之后才能正常使用。注意:Ruby-Memcache 还是不要去试的好(实在、实在慢得让人难以忍受)。

在我自己的项目中,我更倾向于使用基于数据库的 Session 存储,原因是可以使用 Rails 命令行或者数据库软件包提供的管理工具进行简单得管理。对于 MemCacheStore 你就得自己为它编写脚本了。另一方面,对于高访问量网站来说,内存缓存方式的扩展性更好一些,并且它随支持 Session 超时(session expiry)的 Rails 一起提供。

在请求过程中缓存运算结果

假如你在处理一次请求过程中不止一次需要某些相同的数据,但由于你的数据以某种形式依赖于请求参数,而不能使用类级别缓存的时候,那么请将数据缓存起来,以避免重复计算。

要采取这种模式很简单:

module M
  def get_data_for(request)
    @cached_data_for_request ||=
      begin
        expensive computation depending on request returning data
      end
  end
end

你的代码可以是简单的  "A".."Z".to_a ,或者是一个数据库查询,例如取得一个指定的用户等。

在启动或者首次访问时执行与请求无关的运算

这个性能忠告太简单了,我都有点儿犹豫是不是还该把它放在本文中谈论——然而,我还是发现很多我分析过的应用程序并没有采取这项有效的优化技术。

事实上,这个技术再简单不过了:如果你的应用程序里面存在哪些数据,在整个生命周期里都不会发生改变,或者数据改变之少到发生变更之后进行一次服务器重启就可以解决问题的话,那么,请把这些数据缓存在你的应用程序中某个类适当的类变量中。常见的范例如下:

class C
  @@cached_data = nil
  def self.cached_data
    @@cached_data ||= expensive computation returning data
  end
...
end

实际的例子有:

  • 应用程序的配置数据(如果你把应用程序设计成其他人可以安装的方式)
  • 数据库中恒定不变的(静态的)数据(否则使用缓存)
  • 使用 ObjectSpace.each 检测已安装的 Ruby 类/模块

优化查询

Rails 提供了功能强大的领域特定语言(Domain Specific Language),用来定义模型类之间反映数据表关系的关联。哎,可惜目前的实现仍然没有对性能进行优化。依赖于内建生成的访问器会严重地影响性能。p>

首当其冲的问题就是常被大家称作“1 + N”查询的问题:如果你从类 Article(数据表“articles”)加载 N 个对象,而类 Article 到类 Author(数据表“authors”)之间存在多对一的关系,使用生成访问器方法访问一个特定的 Article 将会触发 N 个额外的数据库查询。这自然而然就给数据库带来了额外负担,不过对于 Rails 应用服务器性能来说,更重要的是,要提交的 SQL 查询语句会为已经访问过的对象被重新进行构造。

要解决这个额外开销,你可以像下面一样在你的查询参数中添加一个 :include => :author:

Articles.find(:all, :conditions => ..., :include => :author

这样一来,通过提交单独的 SQL 语句以及立即构造 Author 对象的方式,上述额外开销都能得以避免。这项技术常常被称为“贪婪关联查找(find with eager associations)”,并且它对于其它关系类型同样适用(比如一对一、一对多或者多对多关联)。p>

尽管如此,我们还可以使用一项叫做“携带加载(piggy backing)”的技术来进一步优化多对一关系:来自原数据表的属性连同连接表的属性均由带表连接的 ActiveRecord 对象。因此,一条带连接的查询就可以从数据库全数抓取所需的信息。假设你的视图要显示的只是关联到文章信息的作者名称,你可以把上面的查询替换成:

Articles.find(:all, :conditions => ...,
    :joins => "LEFT JOIN authors ON articles.author_id=authors.id",
    :select => "articles.*, authors.name AS author_name")

此外,假如你的视图显示的是可用的文章字段的一个子集,譬如“title”、“author_id”和“created_at”,那么你得把上述代码改成:

Articles.find(:all, :conditions => ...,
:joins => "LEFT JOIN authors ON articles.author_id=authors.id",
:select => "articles.id, articles.title, articles.created_at, articles.author_id, authors.name AS author_name")

一般说来,仅加载部分对象可以在一定程度上提升查询的速度,尤其在你的模型对象拥有大量字段的时候更是如此。为了使用这项技术达到全面提速,你还得在模型类中定义一个方法,用来访问查询附带的所有属性:

class Articles
...
def author_name
@attributes['author_name'] ||= author.name
end
end

当你编写视图代码时,使用这个模式,你就可以不必去了解原来的查询是否存在连接了。

假如你的数据库支持视图,你可以定义一个仅包含必要信息的视图,这样就不必手动编写复杂的查询代码了。这么做的另外一条好处是,可以为你从连接表内加载的字段获得正确的数据类型转换。到目前为止,Rails 尚未提供这些功能,你必须手动为它们编写代码。

给“潮流前沿的伙计们”的一点补充:翻来覆去捣腾这些相同的模式真是让我颇为厌倦,所以我编写了一些扩展代码用来自动完成大部分这类任务。请到我的博客上查看其预发行版。

避免使用笨拙迟缓的 helper 方法

在 Rails 内核中,有许多 helper 方法运行缓慢。一般而言,所有取 URL hash 作参数的 helper 方法都会调用 routing 模块,来生成引用前基础控制器 action 的最短 URL。这就意味着 route 文件中的几个 routes 应该进行检查,这个过程通常煞费时间。哪怕只是使用像下面一个简单的 route 文件:

ActionController::Routing::Routes.draw do |map|
map.connect '', :controller => "welcome"
map.connect ':controller/service.wsdl', :action => 'wsdl'
map.connect ':controller/:action/:id'
end

你将会发现写成

link_to "Look here for joke #{h @joke.title}",
{ :controller => "jokes", :action => "show", :id => @joke },
{ :class => "joke_link" }

和直接编写这样精简的HTML之间显著的性能差异:

<a href="/jokes/show/<%= @joke.id %>"
class="joke_link">Look here for joke <%= h @joke.title %></a>

对于显示大量链接的页面,我所测得的速度提升比例最高能达到 200%(前提是其它部分已经进行了优化)。

为使模板代码可读性更强,并避免不必要的重复,我通常在 application.rb 内加入生成链接的 helper 方法:

def fast_link(text, link, html_options='')
%(<a href="#{request.relative_url_root}/#{link}"> hmtl_options>#{text})
end

def joke_link(text, link)
fast_link(text, "joke/#{link}", 'class="joke_link"')
end

将上述范例改成:

joke_link "Look here for joke #{h @joke.title}", "show/"

用这种方式来解决 route 生成缓慢的问题显然过于繁琐,一般只有性能要求很高的页面才应当使用。如果你不必马上进行性能调优的话(啊呃,读起来挺像我每天收到的垃圾邮件的感觉),不妨关注我那即将问世的模板优化工具的第一个发行版本,它将在大多数情况下自动帮你完成所有此类工作。

未来 Rails 性能调优的话题

正如上文所说,route 识别和 route 生成的性能需要有些东西来协助解决。我的模板优化工具将解决 route 生成问题。上周又有一个新的 route 实现被整合进 Rails 开发分支中。我进行了一些评测,结果显示,在 route 识别方面,性能似乎得到了一些改善。但就 route 生成而言,结果则比较参差不齐。新的实现是否能改善到一直都比先前版本的速度快,尚且有待观望。

从数据库加载大量 ActiveRecord 对象相对来说是比较慢的,当然程度并不高,因为实际的数据库连接传输开销比较大。不过,由于行数据被表示成用字符串作为键值索引的 hash,在 Rails 内部构造 Ruby 对象的开销相当昂贵。转而使用基于数组的数据行类(array based row class)可以改善这个问题。然而,要做得恰如其分的话,就得牵扯到 ActiveRecord 实现的核心部分,因而我们只能寄希望它在 Rails 2.0 中推出。

最后一点,目前 SQL 查询的构造方式,使得生成查询语句的运算比从数据库获取实际数据的代价更为高昂。要大幅度改善这个现状,我的看法是,可以使绝大多数 SQL 查询只生成一次,然后代入实际参数值进行扩充。当前唯一能解决这个问题的方法只有手动编写你的查询代码。

注:以上关于追求更优性能的可行方案,是我个人的观点,并不代表任何官方“核心团队”对待这些问题的看法。

结语

上文列举的问题并不想给你一种 Rails 可能运行缓慢(乃至于我觉得它太过迟缓)的心里暗示。恰恰相反,我坚信 Rails 是一个卓越的 Web 应用开发框架,对于稳健而又快速的 Web 应用的开发大有裨益,并且带来开发效率的改善。正如所有框架一样,它提供了方便的使用方法,可以大幅度地提升你的开发速度,而且在绝大多数场合下适用于你绝大部分的需求。不过,有些时候,你必须尽可能在每一秒内腾出一些额外的请求,或者你在硬件资源上面受到一定限制,了解如何进行性能调优是大有帮助的。一旦你碰上性能问题,希望本文帮助你理清某些范围的要点,助你一臂之力。

关于作者

Stefan Kaes 是 Rails 性能方面的权威博客RailsExperess 和于 2007 年初发行的《Performance Rails》一书的作者。Stefan 的书将跻身于 Addison-Wesley 新系列丛书 Professional Ruby 的首发阵容。此系列丛书将于 2006 年底面世,包括 Hal Fulton 的《The Ruby Way》第二版,以及丛书编辑 Obie Fernandez 所著的旗舰大作《Professional Ruby on Rails》。该系列将是一套精辟入里的学习工具型丛书,从专业的高度帮助读者最大限度从 Ruby 和 Rails 中汲取精华。

rails性能优化 详细介绍了rails性能优化的方方面面,对于性能调优有一定的帮助。 立即下载

相关推荐

块坐标下降法实战:如何用Python一步步优化无人机通信网络?

本文通过Python实战案例,详细介绍了块坐标下降法在优化无人机通信网络中的应用。文章以最大化网络吞吐量为目标,将复杂的非凸问题拆解为功率分配和轨迹规划两个子问题,并提供了完整的算法实现、代码示例及调参指南,为机器学习工程师解决类似优化问题提供了实用参考。

sat99的博客 975

Ruby社区应该去Rails化了

转自:http://robbinfan.com/blog/40/ruby-off-rails 从Linkedin和Iron.io抛弃ruby说起 最近半年关于Ruby编程语言最负面的两条新闻莫过于2012年10月的报导:Linkedin从ruby迁移到node.js,30台服务器减到3台,以及2013年3月的报导:Iron.io从ruby迁移到Go,30台服务器减到...

weixin_34138521的博客 272

视觉惯性SLAM系列——ORB-SLAM3的整体介绍及各个线程的功能(五)

跟踪线程(Tracking Thread)作用:负责处理每一帧图像,进行相机位姿的估计和定位。它首先执行与前一帧图像的特征匹配,然后通过仅运动(motion-only)BA(Bundle Adjustment)优化位姿。如果跟踪失败,它会尝试在所有地图中进行重定位。此外,跟踪线程还负责决定何时插入新的关键帧,以及何时将关键帧从局部地图中移除。详细介绍见局部建图线程(Local Mapping Thread)作用:管理局部地图,向活跃地图添加关键帧和点,移除多余的关键帧和点。

曾经也曾立志 1004

提升Ruby on Rails性能的几个解决方案

简介 Ruby On Rails 框架自它提出之日起就受到广泛关注,在“不要重复自己”,“约定优于配置”等思想的指导下,Rails 带给 Web 开发者的是极高的开发效率。 ActiveRecord 的灵活让你再也不用配置繁琐的 Hibernate 即可实现非常易用的持久化,Github 和 Rubygems 上丰富多样的 Rails 插件是 Rails 开发高效率的又一有力保障。Rails 是

tongxiaomingicesword的专栏 2405

香橙派5上跑DeepSeek-R1的避坑指南:从模型下载到NPU加速全流程解析

本文详细解析了在香橙派5(RK3588)上部署DeepSeek-R1蒸馏模型的完整流程与优化技巧。内容涵盖从系统配置、RKLLM工具链编译、模型转换到NPU加速调优的全过程,重点分享了如何利用RK3588的NPU算力实现高效推理,并提供了常见问题的解决方案,为边缘设备部署LLM提供了实用指南。

CAT789的专栏 717

线性模型:AR、MA、ARMA、ARMAX、ARX、ARARMAX、OE、BJ等

线性模型:AR、MA、ARMA、ARMAX、ARX、ARARMAX、OE、BJ等

zephyr_wang的博客 1万+

浅析Ruby on Rails部署方案

转自:http://blog.youkuaiyun.com/jrckkyy/article/details/2501136 前言 2006初,我接到了公司分配的一个遗留项目,让我负责一个基于C/S的系统的服务器端。其实是系统是基于HTTP协议的,因为负责客户端的同事对于服务器端编程不甚了解,虽然使用PHP对熟悉C++的他来说是驾轻就熟,但是在进一步实现更多的功能和更高的性能上就捉襟见肘了

dazhi_100的专栏 2963

避开这些坑!QMT多因子策略实盘中的5个常见错误及解决方案

本文深入剖析了在QMT平台上实施多因子策略时常见的5个错误,包括因子过拟合、数据延迟处理不当、因子失效、交易成本低估和风险控制缺失,并提供了实用的解决方案。通过优化因子筛选、数据对齐和动态管理,帮助量化交易者提升策略稳健性和实盘表现。

k5l6m的博客 238

Ruby On Rails 性能提升

1 Introduction简介 大家总是说 Rails 好慢啊,这差不多已经成为 Ruby and Rails 社区里的一个老生常谈的问题了。然而实际上这个说法并不正确。只要正确使用 Rails,把你的应用运行速度提升 10 倍并不困难。那么如何优化你的应用呢,我们来了解下面的内容。 1.1 优化一个 Rails app 的步骤 导致你的 Rails 应用变慢无非以下两

tianxingjian111222的专栏 1806

APB Timer验证环境搭建——学习笔记(1)

以下为个人学习笔记,如有疏漏,请大家指正补充~~APB Timer是一个简单的下行计数器,包括一个32位计数器和一组比较器,可以实现多种计时功能。包括生成一次性中断或者定时中断。

qq_41651535的博客 1578

逆向工程实战:从零解析微信PC版协议,并用开源代码构建你的私有化接口

本文详细解析了微信PC版的通信协议架构,从逆向工程基础到网络流量捕获、关键函数定位与HOOK技术实践,最终通过开源代码构建私有化接口。文章重点介绍了协议特征分析、HOOK技术应用以及安全实践,帮助开发者深入理解企业级通信协议的设计逻辑和安全机制。

weixin_30496999的博客 392

如何提高 Ruby On Rails 性能

大家总是说 Rails 好慢啊,这差不多已经成为 Ruby and Rails 社区里的一个老生常谈的问题了。然而实际上这个说法并不正确。只要正确使用 Rails,把你的应用运行速度提升 10 倍并不困难。那么如何优化你的应用呢,我们来了解下面的内容。 Ruby on rails是一款性能非常强大的Web框架。不过在实际应用中我们还需要不断的改进操作来帮助我们提高对Rub

tianxingjian111222的专栏 795

【PowerBI专栏】PowerBI导入MYSQL数据库

本文介绍了在PowerBI中连接MySQL数据库的方法。首先需要下载并安装MySQL官方.NET连接驱动(推荐8.4.0版本),安装完成后重启PowerBI。连接时需填写服务器IP和数据库名称等必填参数。文章详细说明了连接配置步骤,包括账户设置、数据表选择以及可能遇到的数据格式转换问题处理。最后指导用户完成数据清洗后进入PowerPivot进行后续计算。特别提醒最新9.4版本可能存在兼容性问题,建议使用稳定的8.4版本连接器。

技术与管理的平衡木- 690

Rails性能优化简明指南 (转载)

Rails性能优化简明指南 By YUANYI ZHANG | Published: JULY 21, 2010 最近多背一公斤的网站由于前段时间加了太多功能,但是没有考虑性能问题,导致速度下降,内存占用剧增,apache频频重启,需要进行优化,于是花了点时间学习了下rails的优化,整理成这篇文章,希望对遇到同样问题的人有用。 基本原则 大部分讲优化的文章都会在开头讲到优化的基本原则...

weixin_34006468的博客 192

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

浅析Ruby on Rails部署方案(转载)

前言 2006初,我接到了公司分配的一个遗留项目,让我负责一个基于C/S的系统的服务器端。其实是系统是基于HTTP协议的,因为负责客户端的同事对于服务器端编程不甚了解,虽然使用PHP对熟悉C++的他来说是驾轻就熟,但是在进一步实现更多的功能和更高的性能上就捉襟见肘了。项目是在非常突然的情况下交给我的,因为该同事在客户端上有更多的事情要做。我在分析了他的数据库结构和PHP源代码之后,决定按照与...

xinlanzero 171

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Ruby on Rails开发人员需要掌握的常见性能规则

本文将带领你深入了解HTTP、Ruby on Rails和数据库的最佳实践,探讨何时应遵循规则,以及何时打破规则可能使你的代码脱颖而出。原文链接:https://www.rorvswild.com/blog/2023/everyday-performance-rules-for-ruby-on-rails-developers未经允许,禁止转载!译者 | 弯月责编 |夏萌出品 | 优快云(ID...

优快云资讯 1101

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

Rails 1.2性能不如1.1?

Stefan Kaes自2005年加入Rails核心开发团队,并撰写了InfoQ 2006年最受欢迎的文章《Rails常见性能问题一览》。他的Rails性能测试工具Railsbench刚刚发布了0.9.2版本。Railsbench是一个Ruby和Shell的集合,可以帮助我们轻而易举地测试Rails应用的原始性能。目前对HTTP POST进行基准测试的功能已经得到支持,并有一些新特性加入,这些特性...

啊啊啊啊2 302

Rails应用性能优化1

Rails应用性能优化1 ByYUANYI ZHANG|Published:JUNE 11, 2007 是否觉得你的Rails应用响应速度过于缓慢呢?这是RailsConf2006上的一篇关于Rails应用性能优化的演讲稿,希望能够对你有所帮助。 在优化你的应用之前,我们首先需要明确以下几点: 不先进行性能测试就盲目的优化是非常愚蠢的 如果你的应用是因为设计不合理而导致性...

weixin_30823227的博客 60

rails 创建_如何创建基于组件的Rails应用程序

rails 创建 获取完整的书 基于组件的Rails应用程序:受控制的大型域(Addison-Wesley Professional Ruby系列) 建议零售价$ 44.99 看见 本文摘自Stephan Hagemann撰写的Pearson Addison-Wesley的“基于组件的Rails应用程序”一书,经Pearson©2018许可在此处转载。有关更多信息,请访问no...

cxu0262的博客 656

Rails, Wicket, Grails, Play, Lift, JSP性能比较测试

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34252090的博客 166

Rails 的 SNS 准备

用Rails开发sns网站的经验总结(目录) 在我的博客Railser.cn上会陆续贴出来。下面列出一些提纲。可能不按照这个顺序写。1、 中国省份城市插件(一) 2、服务器端的fckeditor富文本编辑器的使用(1)服务器端的配置,页面修改(2)rails插件和插件的bug(3)插件中实现文件名加密,文件路径加密,中文名问题,上传类型问题3、如何处理action的异常4、多态情况下的多...

weixin_30319097的博客 114
上一篇: rails 中关于 一个form如何修改两个关联表数据
下一篇: wrong number of arguments (2 for 0) 错误
jackdraw
博客等级 码龄20年 1粉丝 1原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值