rails 性能优化心得
2012-05-11 18:46 分类: 技术 近期一个项目要上线(终于要交付了),交付前进行了一些大数据量下的性能优化,心得记录下来:
在开发的初期就要使用大量的数据。10K 比较合适。 有生产数据最好(比如需要跟其他系统进行交互),没有的话,就自己生成(factory girl). 很多时候,打开一个性能糟糕的页面,100条数据下看不出来什么,但是10K数据下就看出速度来了。
使用设计良好的MVC模式。该放在MODEL里的,绝对不要放在CONTROLLER里,更不要放在VIEW里。原因是: 2.1 耦合严重。我们要 decouple. 2.2 很多性能测试工具,难以测试出页面中的方法,但是可以测试出MODEL中的方法。 2.3 放在MODEL中,容易写单元测试(测试代码易于编写,那么实现代码就易于编写)。
另外,最好一次性的读取数据,例如,你需要读取3条记录,最好使用这样的方式: @device = Device.where("id in (1,2,3)") 它会生成这样形式的 SQL: select .... where id in (1,2,3)
而绝对不要在你的页面中 :
[1,2,3].each { |i| Device.where("id = #{i}") }
因为后者会链接数据库 3 次, 而前者只一次。 想一想,如果你的某个循环需要重复链接数据库1000次,该是什么效果? 所以。... 回头再详述一下。(TODO)
WEBSERVICE 用的越少越好。 回头专门详述。 总之,这个是拖累系统速度的毒瘤。能不用就绝对不用,非用不可的话,一定要用缓存。
缓存一定要用。可以不用MEMED CACHE这样重量级的,但是起码轻量级的一定要用。例如: config.cache_store = :memory_store, {:expires_in => 60} 使用 observer与之配合,效果更好。
在上一个项目用,我用cache来缓存系统的常量。它们是可被配置的,保存在数据库中,一般情况下不会被修改。 回头我也写个文章来记述一下。
尽量少与其他 的系统交互。 这个也很拖累性能。(跟webservice 的理由一样,一个response 需要2~4秒,客户体验不好)。 一定要加缓存。 例如,目前这个系统的“authorization" (授权),不是使用cancan或者 本地的数据库表来授权,而是要访问远程的某个服务器,以GET方式读取某个用户的权限。 一般读取一次需要1~2秒,如果某个页面有5个地方需要授权,那么打开这个页面就要消耗10秒左右的时间。 所以,一定要做缓存。。。 TODO 同上面第四点,写个专门的文章。。。
使用SQLITE3的话,不要使用transaction. 可以使用delayed_job. 因为一旦使用了TRANSACTION, 那么很容易出现 锁死的情况。用户看到的就是 500 错误。 这个在生产环境下是不可接受的。 哪怕速度慢一点也没关系,我们用一个沙漏啥的小动画来告诉用户,这个东西在后台处理,需要等多少秒。用户也就知道了:哦,它在正常工作,等一会儿我就可以继续操作了。 (这个特别有效,比如说: 上传100K个数据的 CSV, 对它进行验证,读取,验证重复,导入的话,普通方式1000秒, 使用了transaction 300秒, 但是我们用delayed_job的话,用户1秒后就可以看到响应结果(正常处理。。。啥的文字),就会体验很好。 )
同六,凡是会让用户等待过久的地方,使用DELAYED——JOB。
很多功能实现起来很容易,但是需要对某个功能进行进度控制,结果控制的话,就非常麻烦。例如:
8.1 单纯的导入100K个数据,非常容易。 8.2 要求导入100K 个数据, 要求对某个属性进行验证,使之不能重复,也容易。 8.3 在8.2的基础上,还要求显示结果,成功的有多少,失败的有多少。这个就稍有些复杂了。你的代码开始凌乱,有了坏味道,特别是单元测试不够丰富的时候,原有的良好结构开始破坏。 8.4 在8.3的基础上,你使用了delayed_job,这个时候,要求用户看到进度(比如30%), 这个时候,你的代码的复杂度,是8.1的10倍, 8.2的 5倍。 起码在我目前这个项目中是这样的。
数据的冗余很多情况下也是必要的。例如看这些功能: 9.1 我们要调试100K个设备 9.2 调试完毕后,记录每个设备的调试信息。 9.3 某天,我们删除这些设备。 9.4 管理员查看调试记录,发现很多调试信息都丢失了。为什么?
原来,系统的实现方式,是每个调试信息(debug_info) 对应于 设备(device)。那么一旦 设备 被删掉之后,那么调试信息也就没有了。这个是不被允许的。所以,比较理想的办法,是在每个调试信息中,加上对应设备的信息。 虽然看起来比较冗余,但是这是一种平衡。
我们目前上线的系统中,几乎每个模块都有这样的功能和需求。所以。。。哎。。。
保持开发机器和生产环境的机器 环境要一致。这里的一致包括:
- 操作系统。 哪怕一个是CENTOS 一个是 UBUNTU, 将就吧,但最好一模一样
- 语言版本。 要么都是RUBY 1.8.7,要么都是 RUBY 1.9,要么都是 RVM。
- 数据库, 绝对不能一个是MYSQL, 一个是SQLITE3.
- 最好都能访问一个网络,不要出现一个处于内网一个处于外网,一个能访问GIT一个不能,一个能访问通过域名 ooxx.com 访问 授权服务器,另一个则只能通过 ip 访问。
- 最好VIM的版本都是一样的。不能一个 7.3一个 6.9 ,一个有很多PLUGIN, 另一个没有任何PLUGIN。
- 速度要一致。(生产环境的服务器的 SSH 速度不能太慢,例如不要一个在美国一个在大陆,否则 开发人员SSH 生产服务器之后,敲一下键盘3秒钟之后才看到结果,估计调试一个BUG要走神20次)
以上几点,任何一个不满足都是有麻烦的。
一定使用rails3 的 asset pipeline. 这个效果相当明显。记得最初的项目,我们用了20个JS库, 以及好多个CSS, 每次查看网络通信的时候,发现由于每个JS,CSS,IMAGE都要让客户端/服务器重新发送一个 request/response,时间占用的很多。使用了asset pipeline之后,性能改善太多,最显著的就是:只有一个 JS, 一个 CSS 文件。而且RAILS3提供的文档(优化 APACHE, NGINX )也对缓存非常有效。现在图片也缓存的非常好。 打个比方, 原来需要消耗10秒才能发送完毕CSS/JS/图片,现在只需要2~5秒左右。太棒了。
一定要使用coffee javascript. 这个不但提高开发效率,还提高执行效率,还能提供压缩。 想起了 HIBERNATE V。S。 RAW SQL。