`
bonny
  • 浏览: 76363 次
  • 性别: Icon_minigender_1
  • 来自: SH
社区版块
存档分类
最新评论

rails 系统性能优化之路【ZT】

阅读更多
篇文章讲述的是我们在一个Rails on Jruby系统的性能优化之路上披荆斩棘的故事。

优化之前

在开始性能优化之前,有几点必须明确:

1. 性能优化的对象:并不是所有页面都需要优化,而且首先应该选择那些访问率最高、性能瓶颈最大的页面来进行优化。

2. 性能优化的目标:性能优化必须有一个具体的目标,即要达到的响应时间和吞吐量。有了目标,我们就知道目前离目标的距离,需要优化的力度;同时,也知道在何时停止优化。

3. 伴随优化的测量:没有测量的性能优化很可能让你缘木求鱼。

优化之路

下面,分别从多个方面看一下性能优化中的一些实践。

缓存

缓存是性能优化的一大利器。Rails本身对缓存机制有很好的支持。

客户端缓存

一般而言,对于客户端缓存的原则是:对动态html页面不作任何缓存,永久缓存任何其它类型的文件。

Rails很好地支持了这个原则。比如:

stylesheet_link_tag(”application”)

生成的页面元素是:

<link href=”/stylesheets/application.css?1232285206″ media=”screen” rel=”stylesheet” type=”text/css”/>

大家都注意到在css文件后面有个时间戳,这其实是一个小计策:当文件发生改变的时候,时间戳也发生了改变;而客户端会认为这是一个新链接,就会重新获取文件。有了这种机制的支持,我们可以很放心地在客户端对静态文件进行永久缓存,而不用担心过期问题。

服务端缓存

Rails提供了三种服务器端缓存方式:page cache,action cache和fragment cache。对于动态页面,fragment缓存使用的机会会比较多。

让我们看一个例子:一个产品页面片段,片段上面的部分信息对于所有用户都是一致的,但另一部分对于不同权限用户是不一致的。在admin登录时,能看到a链接,b按钮和c复选框;在一般用户登录时,能看到b按钮和c复选框;未登录用户只能看到c复选框。

一种很简便的缓存策略是根据用户标志进行缓存,即对用户p1缓存,对p2也进行缓存,那么我们就可以根据用户的标识以及产品的标志来定位缓存。

但是,这种缓存策略存在一个问题,即缓存数量太大:同一个产品片段存在n份不同的拷贝,n = admin用户的数量 + 一般用户的数量 + 1 (未登录用户)。而且,对于同一种权限的用户来说,他们对同一个产品的缓存是一样的。

消除这种重复的策略是根据用户的类型来对缓存进行分类,那么对于一个产品而言,它只会有3份并且不重复的缓存,3 = 1 (admin权限) + 1 (一般权限) + 1 (未登录用户)。

这 可能是一个比较简单的例子,比较容易想到。但在一些很复杂的情况下,可能就会迷惑。其实原则就是:消除重复,根据片段的根本特征差别来对缓存进行分类。但 这里引起了另外一个问题:如果根本特征的区别需要对很多数据进行大量的计算,那么缓存就失去了它的意义。所以,要把握好权衡。缓存,最重要的是为了减少数 据的重复获取,减少重复计算。

另外,缓存的清理策略是至关重要的:何时清除缓存,以及如何定位失效缓存。对于要求实时性的页面,可以使用 rails提供的sweeper机制来进行缓存清理。但sweeper有个不方便的地方是需要对相关的action都逐一进行声明。我们的系统巧妙地利用 了rails的observer:在observer检测到数据的更新时对相关缓存进行清理。

计算结果缓存

在一个request的生命周期之内,有些数据不会改变,或者我们不关心改变,则可以通过对结果缓存以避免重复计算。

def length
@length ||= end – start
end

内存

来看个例子:我们要在一个页面中显示一百个产品的信息,产品信息从一个信息搜索平台获取。下面的代码是从搜索结果数据集创建产品对象:

records.map { |record| Product.new record }

但这里有个问题,Product是一个ActiveRecord类,但页面显示结果并不需要这么“重量级”的对象。取而代之以轻量级的对象会减少内存的消耗,并提升速度。

数据库

性能的瓶颈往往不在语言级别,而在IO上。对数据库的优化是重中之重。

在数据库的优化上有一些耳熟能详的方法,比如:

增加适当的索引以加快查询。有些很好的工具可以帮助我们找到性能的瓶颈,比如分析execution plan。
缩小transaction的粒度以减少锁的见时间。
避免多次创建transaction的开销(如下代码)
Product.transaction do
search_results.each do |search_result|
Product.create(search_result)
end
end

大型查询

对于像报表类的大型查询,要绕过ActiveRecord,而直接使用数据库驱动。同时,对于多表连接的查询,可以考虑几种方法来优化:

引入中间表,让另外一个进程定时把查询结果插入中间表。但这会牺牲一定的实时性。
避免重复结果的获取,减少结果集。这往往出现在一对多表之间,连接会导致“一”这边出现多条重复结果。可以考虑把一个查询分拆成多个查询。
使用存储过程或者functions。当然,这失去的是业务逻辑的透明性和灵活性。
Rails的finder

接下来,探讨一下如何正确使用Rails的finder。Rails的finder是很容易误用或者不合理使用的地方,往往有很多性能问题因此而起。

在使用finder时,要搞清楚几个问题:

是否需要结果集中的所有数据?
是否需要预先加载?
表的扫描是邪恶的,很吃性能。一定要减少获取的结果集,用:select指定需要的字段。配合以创建正确的数据库索引,并在索引上挂载其它需要的字段,避免表的扫描。

正确使用预先加载可以避免n+1查询:

Company.all(:include => :products, :conditions => “company.kind = ‘toy’”)

产生的sql查询是:

SELECT * FROM companies WHERE kind = ‘toy’

SELECT * FROM products WHERE products.company_id IN (12, 423, 431…)

但错误使用预先加载是个很危险的事情,它可能不会影响结果的正确性,但会引起很严重的性能问题:

Company.all(:include => :products,  :conditions => “products.id IS NOT NULL AND products.weight > 10″)

其实写这个查询的人的目的是为了找出拥有products,并且products的weight大于10的company。但这个语句导致的sql查询是性能低下的:

SELECT companies.id AS t0_r0, …., products.id as t1_r0, … FROM companies
LEFT OUTER JOIN products ON products.company_id = companies.id
WHERE products.id IS NOT NULL AND products.weight > 10

这个sql查询有两个问题:

结果集中的products信息是不需要的
LEFT OUTER JOIN的性能劣于INNER JOIN
我们可以使用如下的语句来避免这两个问题:

Company.all(:joins=> “INNER JOIN products ON products.company_id = companies.id“, :conditions => “products.weight > 10“)

它生成的sql是:

SELECT companies.* FROM companies
INNER JOIN products ON products.company_id = companies.id
WHERE products.weight > 10

这个查询的效率会高很多。所以,正确使用Rails的finder是至关重要的。

页面

View的helper方法生成html元素,比如:

link_to “My Company”, company_path(@company)

# => <a href=”/companies/1″>My Company</a>

过度使用helper方法会引起性能问题。比如上面这个方法,每次都会调用link_to和company_path — 从routes中解析path。

但注意,只有在过度使用的时候,才需要考虑是否可以减少helper方法的使用来提升性能。同时,也可以用一些工具替代ERB来提升性能,比如erubis:它通过预处理避免每次调用的重复开销。

多线程

版本2.2之后,Rails终于是线程安全的了,意味着我们可以开启多线程模式,这绝对是一个巨大的进步。

非线程安全的Rails,无法有效利用共享资源。在以前的版本中,假如我们的应用部署在mongrel上,那么对于n个并发请求,就需要n份rails,n份application,n个数据库链接等等。几乎所有的东西都无法共享。

多 线程模式,对于Rails on JRuby带来的改变尤其巨大,因为jruby的线程是native thread(相对于ruby的green thread)。比如我们的系统只开启了一个应用实例来处理所有的请求(当然,当瓶颈出现在一些共享资源上时,可以考虑增加实例)。

还是用数字来说话吧:Rails on JRuby的内存使用是原先非线程安全时的1/n (n是并发请求的数量),是Rails on Ruby的线程安全模式的1/m(m是cpu的数量)。

那么,开启多线程模式要注意什么?如果你的应用中有类变量的使用,请注意它们是否会在并发下出现问题。

服务器

服务器的配置优化对性能的影响很大。我们的系统以war包的形式部署在tomcat上,主要的配置优化在于对JVM的内存分配上。可以从这几个方面看:

xms — 初始内存大小是否合适?如果你的系统在一开始的时候就需要较大的内存分配,就可以设置一个合理的xms值。
xmx — 最大内存大小是否合适?如果太小,会导致OutOfMomery,会导致持续的GC;反之,则会导致一次GC时间过长,在这段时间内,系统的性能将会受到影响(当然,这跟GC的算法有关)。

性能优化的几个原则

更近

数据库,应用服务器,web服务器以及客户端,这是信息传输的一条链(当然,有些系统的链会更长,更复杂)。那么,减少响应时间的一个原则就是:让数据离客户端更近。

一个最能体现更近原则的优化就是客户端缓存–客户端是距离用户最近的地方。

更加细节的一些例子,比如sql server的nonclustered index现在可以把非键的数据挂载在索引的叶子节点上,这样就不需要再去表上扫描获取这些数据。这也是更近原则的一个体现。

更快

更加快速的响应,需要更加快速的计算。前文中提到的加速页面元素的生成,就属于更快原则。

这方面的实践包括有算法优化,数据库索引,使用更加高效的方法,使用正则表达式匹配等等。

举个Rails中的例子:使用Model.find_by_*方法是很低效的,因为它需要调用method_missing来动态生成方法。而Model.find_by_sql方法的效率高很多。

更少

传输过多的数据,进行过多的操作,可能都会影响性能。

减少数据的传输量有很多例子:压缩静态文件,以减少服务器和客户端之间的传输量;避免在action中滥用实例变量,以减少实例变量在action和view之间的传输;正确合理使用finder,以减少从数据库中获取的数据量等等。

减少操作次数的例子有:合理运用预先加载,减少查询次数;减少transaction的不必要重复创建等等

其它还有:缩小transaction的粒度;缩小并行运算锁的粒度等等。

平衡

在追求更近、更快、更少的时候,要注意平衡。

比如给JVM分配一个合理大小的内存,而不是过大或者过小;不要滥用数据库索引,要考虑是否会影响插入数据的速度;平衡一个运算在空间和时间上的消耗;保持性能在长时间内的平稳等等。

性能优化是一个不断实践、不断调优的过程。比如前文中提到的服务器端缓存,最后并没有被采用,因为我们发现相比而言缓存命中的开销反而更大。

小结

在经过一系列的优化之后,我们的系统很好地满足了客户对性能的要求。下面是几点总结:

大多数性能问题都出在IO上,IO应是关注的重点。
性能优化是一个实践的过程,空讲理论是没有意义的。
出现性能问题,先不要怪罪于平台、语言、框架,大多数性能问题都产生于错误或者不合理的实现。
性能优化过程并不一定需要贯穿整个项目的始终,但一定要时刻保持对性能问题的关注:从刚开始的架构设计,到项目开发中的代码编写、重构等等,性能都应该是关注的一个方面。


http://huzhenbo.name/blog/2010/01/16/rails-performance-tuning/
分享到:
评论

相关推荐

    rails性能优化

    Rails性能优化是一个涉及多个方面的复杂过程,它要求开发者对Ruby on Rails框架的内部机制有深刻的理解,并且能够合理地应用各种技术和工具来提升应用的性能。在性能优化的过程中,首先应该避免盲目优化,而是要通过...

    Ruby on Rails:Rails性能优化与缓存策略.docx

    Ruby on Rails:Rails性能优化与缓存策略.docx

    对优化Ruby on Rails性能的一些办法的探究

    随着Web应用程序变得越来越复杂,性能优化成为了确保用户满意度和提高系统效率的关键步骤之一。Ruby on Rails(简称Rails),作为一个流行的Web开发框架,虽然提供了丰富的功能和快速的开发体验,但也可能会面临性能...

    Complex Rails system_Rails_优化_

    在构建复杂的Rails应用时,性能优化是至关重要的。...理解系统的工作原理,针对性地进行优化,是提升复杂Rails系统性能的关键。在实践过程中,持续监控、分析和调整,才能确保应用始终处于最佳状态。

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

    Ruby On Rails 框架自它提出之日起就受到广泛关注,在“不要重复自己”,“约定优于配置”等思想的指导下,Rails 带给 Web 开发者的是极高的开发效率。 ActiveRecord 的灵活让你再也不用配置繁琐的 Hibernate 即可...

    ruby on rails在线考试系统

    10. 性能优化:Rails应用可以通过缓存、数据库索引、数据库连接池、延迟加载等技术提升性能。在线考试系统可能会大量使用缓存来减少数据库查询,提高响应速度。 以上是关于“ruby on rails在线考试系统”的主要知识...

    rails敏捷开发的购物车系统

    在本文中,我们将深入探讨如何使用Rails敏捷开发技术构建一个购物车系统,特别是在参考《rails敏捷开发第四版》中的示例。Rails 3.2.6是本文的基础框架,它是一个强大的Ruby Web应用程序框架,以其MVC(模型-视图-...

    配置高可用的rails

    总结上述知识点,在构建高可用的rails应用时,需要综合考虑多个组件的配置,以及对性能的持续监控与优化。整个架构需要确保在单点故障情况下应用的持续可用,同时还要有良好的扩展性和维护性。对于有一定Ruby基础的...

    Ruby-Rails实战之B2C商城开发

    10. 性能优化:使用缓存(如Rails的Page Cache、Action Cache)、数据库索引、资产打包(如Webpacker或Sprockets)等技术提高网站性能。 在这个项目中,文件夹`master_rails_by_actions-master`可能包含了整个商城...

    RoR性能优化经验谈

    总之,RoR性能优化是一个全面的过程,涵盖从操作系统到Web服务器配置,再到代码本身的改进。每个环节的优化都能显著提升网站的运行效率,使RoR应用能够更好地应对高负载和大规模用户的需求。通过学习和实践这些经验...

    rails2-sample

    这一部分将覆盖一些高级的Rails主题,如性能优化、多线程和并发处理、部署策略等。对于想要深入了解Rails框架并构建高性能Web应用的开发者来说,这些知识是必不可少的。 #### 10. Rails Plugins(Rails插件) ...

    基于Ruby On Rails的在线购书系统-毕业设计基于Struts+Hibernate的人力资源管理信息系统

    【标题】中的“基于Ruby On Rails的在线购书系统”是指使用Ruby编程语言以及Ruby on Rails框架开发的一个电子商务平台,特别关注在线书籍销售。Ruby on Rails(简称RoR)是Web应用开发的一个开源框架,它遵循MVC...

    Rails进行敏捷Web开发(所有版本的源码rails3.0-4.0)

    3. Rails 3.2: Rails 3.2进一步优化了性能,引入了低延迟的LogSubscriber,提升了日志记录的效率。ActiveRecord的查询性能得到提升,例如添加了`pluck`方法,可以直接获取数据库列的值。此外,`rails generate ...

    Rails101_by_rails4.0

    《Rails101_by_rails4.0》通过 Lean Publishing 的出版流程,让书籍在发布过程中能够不断吸纳读者的反馈,通过多次迭代优化内容,直至形成一本高质量的自学教程。Lean Publishing是指利用轻量级的工具,快速出版一...

    基于Ruby On Rails的在线购书系统

    学习如何配置Nginx或Apache,使用Capistrano进行部署,以及监控和优化应用性能。 9. **前端技术**:虽然主要讨论后端,但前端界面也是购书系统的重要组成部分。可能涉及HTML、CSS、JavaScript,以及使用Bootstrap或...

    Rails 101 入门电子书

    ### Rails 101 入门电子书知识点详解 #### 一、简介 《Rails 101 入门电子书》是一本非常适合初学者直接入门的书籍,它由xdite编写并出版于2014年6月10日。本书主要针对的是希望学习Ruby on Rails框架的读者,特别...

    Advanced Rails

    1. **优化性能**:Rails应用在处理大量请求时可能会面临性能挑战。书中会介绍如何通过缓存(如Action Cache和Page Cache)、数据库查询优化、资产管道优化等手段提升应用性能。 2. **复杂的路由**:Rails的路由系统...

    关于rails 3.1 cucumber-rails 1.2.0

    Rails 3.1 和 Cucumber-Rails 1.2.0 是两个在Web开发领域非常重要的工具,尤其对于Ruby on Rails框架的测试和自动化流程。本文将深入探讨这两个组件,以及它们如何协同工作来增强软件开发的效率和质量。 首先,...

Global site tag (gtag.js) - Google Analytics