怎样提升 RailS 应用的性能?
Is rails slow?
「铁路非常慢」,你或许听过这个笑话,那么我们的 Rails 框架呢?
假设说 Rails 慢,那么怎样提升 Rails APP 的性能就成了开发人员们最关注的问题。
或许你听说过非常多提升 RoR APP 性能的方法,它们有难有易。我们须要在选择其中最能帮助开发人员脱离性能困境的。
这里列举了几种不同的提升 Rails 应用性能的方法。
1. 数据库索引
你的 APP 被 DB 性能限制,优秀的数据库索引能够在大型数据库表中带给你100倍的性能提升。
然而并不是全部 Rails 开发人员都明确这一点有多重要。
加入 indexes 非常easy:
class AddIndexToClientIndustry < ActiveRecord::Migration
def change
add_index :client_industries, :client_id
end
end
接下来就有无 Index 的情况做个对照。
有 Index 的情况:
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 的情况:
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秒。
因此project师能够在全部引用參数。或者其它经常查询的參数中加入 Indexes。
可是不能加太多, 由于每个都会添加 DB Size 从而影响性能。
2. 数据库查询数量
RoR让编程更快捷。但反过来也让每条请求的数据库查询次数难以控制。举个样例,假设每个 Client 有一或多个 Industries。 我们想要显示 Client List 和它们的 Primary Industries:
<% @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条数据库查询:
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
# app/controllers/clients_controller.rb
def index
@clients = Client.includes(:industries).all
end
如今仅仅有2至3条数据库查询而非51条:
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:
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:
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:
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 应用内部完毕应用性能管理和监控,包含代码级别性能问题的可见性、性能瓶颈的高速识别与追溯、真有用户体验监控、server监控和端到端的应用性能管理。 追溯性能瓶颈至:性能表现差的 SQL 语句、第三方 API、Web Services、Caching Layers、后台任务等。
图为使用 OneAPM 进行监控的总览页面,在这里能够对请求在server端耗时有个初步印象。能够直观的看到不同一时候间 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 project师编译整理。OneAPM 是应用性能管理领域的新兴领军企业,能帮助企业用户和开发人员轻松实现:缓慢的程序代码和 SQL 语句的实时抓取。
想阅读很多其它技术文章。请訪问 OneAPM 官方博客。
怎样提升 RailS 应用的性能?的更多相关文章
- 智能SQL优化工具--SQL Optimizer for SQL Server(帮助提升数据库应用程序性能,最大程度地自动优化你的SQL语句 )
SQL Optimizer for SQL Server 帮助提升数据库应用程序性能,最大程度地自动优化你的SQL语句 SQL Optimizer for SQL Server 让 SQL Serve ...
- [转载]Hibernate如何提升数据库查询的性能
目录(?)[-] 数据库查询性能的提升也是涉及到开发中的各个阶段在开发中选用正确的查询方法无疑是最基础也最简单的 SQL语句的优化 使用正确的查询方法 使用正确的抓取策略 Hibernate的性能优化 ...
- 使用 JDBC 操作数据库时,如何提升读取数据的性能?如 何提升更新数据的性能?
要提升读取数据的性能,可以指定通过结果集(ResultSet)对象的 setFetchSize() 方法指定每次抓取的记录数(典型的空间换时间策略):要提升更新数据的性能 可以使用 PreparedS ...
- Databricks缓存提升Spark性能--为什么NVMe固态硬盘能够提升10倍缓存性能(原创)
我们兴奋的宣布Databricks缓存的通用可用性,作为统一分析平台一部分的 Databricks 运行时特性,它可以将Spark工作负载的扫描速度提升10倍,并且这种改变无需任何代码修改. 1.在本 ...
- 用分布式缓存提升ASP.NET Core性能
得益于纯净.轻量化并且跨平台支持的特性,ASP.NET Core作为热门Web应用开发框架,其高性能传输和负载均衡的支持已广受青睐.实际上,10-20台Web服务器还是轻松驾驭的.有了多服务器负载的支 ...
- Java: 扩大字节缓存区的大小,提升AIO的处理性能(并发性能)
前些日了,对AIO与NIO的并发性能进行了比较,在低并发的情况下,NIO性能表现比AIO好一些,主要原因是,NIO中可以使用FileChannel.transferTo(long position, ...
- 如何提升mysql replication的性能&多线程传输二进制日志
1,最好使用内网或者专线链路传输binlog数据 (千兆网卡.还不够的话,bounding 技术,扩展带宽) 在my.cnf中强制使用内网ip传输数据bind-address=ip2,将二进制保存在独 ...
- 使用RSS提升DPDK应用的性能(转)
本文描述了RSS以及在DPDK中如何配置RSS达到性能提升和统一分发. 什么是RSS RSS(Receive Side Scaling)是一种能够在多处理器系统下使接收报文在多个CPU之间高效分发的网 ...
- How to improve Java's I/O performance( 提升 java i/o 性能)
原文:http://www.javaworld.com/article/2077523/build-ci-sdlc/java-tip-26--how-to-improve-java-s-i-o-per ...
随机推荐
- python小小面试题
一.python是如何进行内存管理的?Python引入了一个机制:引用计数.python内部使用引用计数,来保持追踪内存中的对象,Python内部记录了对象有多少个引用,即引用计数,当对象被创建时就创 ...
- .NET Core多平台项目模板eShopOnContainers编译手记
之前写了一个功能性的文件上传asp.net core的小程序,加上点七七八八的东西,勉强能够应付了,打算学习一下微软的官方.NET CORE微服务示例https://github.com/dotnet ...
- "use strict"详解
参考:http://www.ruanyifeng.com/blog/2013/01/javascript_strict_mode.html 目的: 消除JavaScript语法的一些不合理.不严谨之处 ...
- 使用 ConfigSource 特性 拆分 Web.config 文件
一个大项目里可能会有非常多个配置参数,有.Net自己支持的配置(比如WCF,AppSettings),还有一部分是自定义的配置(比如继承自ConfigurationSection和Configurat ...
- Nginx-动态路由升级版
前几篇文章我们介绍了Nginx的配置.OpenResty安装配置.基于Redis的动态路由以及Nginx的监控. Nginx-OpenResty安装配置 Nginx配置详解 Nginx技术研究系列1- ...
- java多线程创建-Thread,Runnable,callable和threadpool
java创建多线程的方式有许多种,这里简要做个梳理 1. 继承Thread类 继承java.lang.Thread类,创建本地多线程的类,重载run()方法,调用Thread的方法启动线程.示例代码如 ...
- canvas图表(2) - 折线图
原文地址:canvas图表(2) - 折线图 话说这天气一冷啊, 就患懒癌, 就不想码代码, 就想着在床上舒舒服服看视频. 那顺便就看blender视频, 学习下3D建模, 如果学会了建3D模型, 那 ...
- short s1 = 1; s1 = s1 + 1;有错而short s1 = 1; s1 += 1正确。为何?
如果你认为表达式(x += i)只是表达式(x = x + i)的简写方式,这并不准确.这两个表达式都被称为赋值表达式.第二个表达式使用的是简单赋值操作符(=),而第一个表达式使用的是复合赋值操作符. ...
- ELK 快速指南
ELK 快速指南 概念 ELK 是什么 ELK 是 elastic 公司旗下三款产品 ElasticSearch .Logstash .Kibana 的首字母组合. ElasticSearch 是一个 ...
- [Machine Learning]学习笔记-Neural Networks
引子 对于一个特征数比较大的非线性分类问题,如果采用先前的回归算法,需要很多相关量和高阶量作为输入,算法的时间复杂度就会很大,还有可能会产生过拟合问题,如下图: 这时就可以选择采用神经网络算法. 神经 ...