怎么提高RailS应用的性能

Is rails slow?

「铁路很慢」,你也许听过这个笑话,那么我们的Rails框架呢? 如果说Rails慢,那么如何提升Rails APP的性能就成了开发者们最关注的问题。

也许你听说过很多提升RoR APP性能的方法,它们有难有易,我们需要在选择其中最能帮助开发者脱离性能困境的。

这里列举了几种不同的提升Rails应用性能的方法。

1.数据库索引

你的APP被DB性能限制,优秀的数据库索引可以在大型数据库表中带给你100倍的性能提升。然而并非所有Rails开发者都明白这一点有多重要。

添加indexes很容易:

1
2
3
4
5
class  AddIndexToClientIndustry < ActiveRecord::Migration  
    def change  
      add_index :client_industries, :client_id  
    end  
end

接下来就有无 Index 的情况做个对比。

有 Index 的情况:

1
2
3
4
5
6
7
8
9
10
11
CREATE INDEX   
  addresses_addressable_id_addressable_type_idx  ON  
  addresses  USING btree  (addressable_id,addressable_type);  
  t1 = Time.now  
   c = Company.find( 178389 )  
   a = c.addresses.first  
  t2 = Time.now  
  puts "---Operation took #{t2-t1} seconds---”  
   
  Result with index:  
  ---Operation took  0.012412  seconds---

没有 Index 的情况:

1
2
3
4
5
6
7
8
9
10
11
DROP INDEX  
  addresses_addressable_id_addressable_type_idx;  
   
t1 = Time.now  
  c = Company.find( 178389 )  
  a = c.addresses.first  
t2 = Time.now  
puts "---Operation took #{t2-t1} seconds---”  
   
Result without index:  
---Operation took  0.378073  seconds---

0.378073 / 0.012412 = 30.46 没有索引比有索引慢了30.46秒。

因此工程师可以在所有引用参数,或者其他经常查询的参数中加入Indexes。但是不能加太多,因为每一个都会增加DB Size从而影响性能。

2.数据库查询数量

RoR让编程更快捷,但反过来也让每条请求的数据库查询次数难以控制。举个例子,如果每一个Client有一或多个Industries。我们想要显示Client List和它们的Primary Industries:

1
2
3
4
5
6
7
8
9
10
11
12
<%  @clients .each  do  |client| %>  
   <tr>  
       <td><%= client.id %></td>  
       <td><%= client.business_name %></td>  
       <td><%= client.industries.first.name %></td>  
   </tr>  
<% end %>  
   
# app/controllers/clients_controller.rb  
def index  
     @clients  = Client.all  
end

如果有50个Clients, 则会有51条数据库查询:

1
2
3
4
5
6
Processing by ClientsController#index as HTML  
SELECT  "clients" .* FROM  "clients"   
SELECT  "industries" .* FROM  "industries"  INNER JOIN  "client_industries"  ON  "industries" . "id"  "client_industries" . "industry_id"  WHERE  "client_industries" . "client_id"  1  LIMIT  1  
SELECT  "industries" .* FROM  "industries"  INNER JOIN  "client_industries"  ON  "industries" . "id"  "client_industries" . "industry_id"  WHERE  "client_industries" . "client_id"  2  LIMIT  1  
SELECT  "industries" .* FROM  "industries"  INNER JOIN  "client_industries"  ON  "industries" . "id"  "client_industries" . "industry_id"  WHERE  "client_industries" . "client_id"  3  LIMIT  1  

解决方案: Eager Loading

1
2
3
4
# app/controllers/clients_controller.rb  
def index  
     @clients  = Client.includes(:industries).all  
end

现在只有2至3条数据库查询而非51条:

1
2
3
4
5
6
Processing by ClientsController#index as HTML  
SELECT  "clients" .* FROM  "clients"   
SELECT  "client_industries" .* FROM  
"client_industries"  WHERE  
"client_industries" . "client_id"  IN ( 1 2 3 )  
SELECT  "industries" .* FROM  "industries"  WHERE  "industries" . "id"  IN ( 1 5 7 8 4 )

3.减少内存占用

只用真正需要的gem

使用时再加载对象

分批处理海量数据。

一个使用真实数据的例子——find_each:

Using find:

1
2
3
4
5
6
t1 = Time.now    
Company.where(:country_id=> 1 ).find  do  |c|  
puts  "do something!"  if  [ 'Mattski Test' ].include?(c.common_name)  
end  
t2 = Time.now  
puts "---Operation took #{t2-t1} seconds---”

Result:

1
query, taking  46.65  seconds

Now using find_each:

1
2
3
4
5
6
t1 = Time.now    
Company.where(:country_id=> 1 ).find_each  do  |c|  
       puts  "do something!"  if  [ 'Mattski Test' ].include?(c.common_name)  
end    
t2 = Time.now  
puts  "---Operation took #{t2-t1} seconds---"

Result:

1
100  queries, taking  15.53  seconds in total (3x faster)

也有查询多了反而快的情况。

4.使用缓存

缓存的使用对性能有巨大影响,首先确保数据模型正确,缓存可以帮你隐藏结构问题。

对象缓存 在使用对象缓存的情况下,应该把查询方法的include去掉,避免关联查询无法利用缓存的现象。

查询缓存 在不要求实时的情况下,对于统计类耗时查询,那么可以使用memcache-client将查询结果缓存到memcached里。

页面局部缓存 对象缓存和查询缓存都会降低数据库访问负载,但如果RoR的负载很高,就只能依靠页面局部缓存了。

「web2.0网站比较常用使用页面局部缓存,Rails的页面局部缓存有一个缺点,就是和页面查询结果对应的Action当中的查询语句要放在View里面,否则每次Action里面的查询还是会被执行,但是这样做会破坏程序代码良好的MVC结构。这种情况下,也可以采用另外一个Cache插件: better rails caching,在缓存页面的同时可以缓存Action当中的查询语句。」

5.让Web请求更快

只有少量可用进程用于服务web请求,因此需要使web请求更快。理想情况下,web进程一般在毫秒内完成,1至2秒算是慢的,10秒以上是非常慢的。如果你的web请求很慢,你的Rails APP将无法支撑同一时间的大量用户。

解决方案:使用后台运行对长时间运行的项目使用后台运行诸如delayed jobs, 从而释放你的web进程来解决更多请求。

6.性能监控

对APP进行性能监控从而便于发现哪部分运行的慢,甚至快速定位到问题所在,可以利用国内应用性能监控做的最好的OneAPM监控工具。

OneAPM for Ruby能够深入到所有Ruby应用内部完成应用性能管理和监控,包括代码级别性能问题的可见性、性能瓶颈的快速识别与追溯、真实用户体验监控、服务器监控和端到端的应用性能管理。追溯性能瓶颈至:性能表现差的SQL语句、第三方API、Web Services、Caching Layers、后台任务等。

图为使用OneAPM进行监控的总览页面,在这里可以对请求在服务器端耗时有个初步印象。可以直观的看到不同时间web事物、后台任务、数据库和外部服务的平响应时间、吞吐量、执行次数等指标,图中web事物在15:41的时候响应时间出现峰值,响应速度较慢。

为了进一步确定问题所在,点进web事物界面可以进一步了解各慢事物响应时间占比,快速定位到api/medicines/index的响应时间较长。

点击错误的请求地址,将会列出该错误的URL、第一次和最后一次发生时间、错误发生次数、监测到错误的Agent名称、错误信息和堆栈信息。

好的应用性能监控往往需要花大量的时间和精力实现,因此选择优秀的第三方监控工具将极大地提高运维效率,这对提升Rails APP性能有极大帮助。

7.使用内存数据库

当查询和排序都在内存中完成,数据库将会运行的更快,而它们需要在磁盘上运行的时候就变得很慢。

解决方案:

限制DB的大小,保证它完全适合内存。

将不紧急的信息移出主要数据库,移入次要数据库或其他地方。

如果有大量存储需求,考虑使用非关系型数据库。

8.更多性能建议:

对静态文件使用内容分发网络,例如使用AWS CloudFront。

对需要1-2秒的加载项使用延迟加载。

使用服务导向架构,使一些进程在托管栈同步进行。

相信选择一种或几种适合的性能提升方法,可以使RoR APP更令用户满意。

备注:本文参考并翻译了Matt Kuklinski在slideshare上关于提升Rails性能所分享的部分内容。

本文系OneAPM工程师编译整理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
4S店客户管理小程序-毕业设计,基于微信小程序+SSM+MySql开发,源码+数据库+论文答辩+毕业论文+视频演示 社会的发展和科学技术的进步,互联网技术越来越受欢迎。手机也逐渐受到广大人民群众的喜爱,也逐渐进入了每个用户的使用。手机具有便利性,速度快,效率高,成本低等优点。 因此,构建符合自己要求的操作系统是非常有意义的。 本文从管理员、用户的功能要求出发,4S店客户管理系统中的功能模块主要是实现管理员服务端;首页、个人中心、用户管理、门店管理、车展管理、汽车品牌管理、新闻头条管理、预约试驾管理、我的收藏管理、系统管理,用户客户端:首页、车展、新闻头条、我的。门店客户端:首页、车展、新闻头条、我的经过认真细致的研究,精心准备和规划,最后测试成功,系统可以正常使用。分析功能调整与4S店客户管理系统实现的实际需求相结合,讨论了微信开发者技术与后台结合java语言和MySQL数据库开发4S店客户管理系统的使用。 关键字:4S店客户管理系统小程序 微信开发者 Java技术 MySQL数据库 软件的功能: 1、开发实现4S店客户管理系统的整个系统程序; 2、管理员服务端;首页、个人中心、用户管理、门店管理、车展管理、汽车品牌管理、新闻头条管理、预约试驾管理、我的收藏管理、系统管理等。 3、用户客户端:首页、车展、新闻头条、我的 4、门店客户端:首页、车展、新闻头条、我的等相应操作; 5、基础数据管理:实现系统基本信息的添加、修改及删除等操作,并且根据需求进行交流信息的查看及回复相应操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值