Rails Rails 5 - 将会有更快的 render collection 以及优化小细节

huacnlee · May 25, 2015 · Last by huacnlee replied at June 18, 2015 · 9831 hits
Topic has been selected as the excellent topic by the admin.

https://dev.firmafon.dk/blog/rails-5-much-faster-collection-rendering/

Rails 5 将会带来更快的 collection render fragment (有 cache 的). Pull Request 已经合并了:https://github.com/rails/rails/pull/18948

原理是内部用了 Cache multi read,能有效减少 Cache 服务的连接,从而加快速度。

用法

<%= render partial: 'item', collection: @items, cache: true %>

或者

<%= render partial: 'item', collection: @items, cache: Proc.new{|item| [item, 'show']} %>

在 Rails 5 发布前,大家可以用 multi_fetch_fragments。 已经在 Ruby China 上面用上,并刚刚上线了哦。

https://github.com/ruby-china/ruby-china/commit/dde8d199292571f9dd262c62b1495d58f2374214


题外话

循环 render partial vs render :collection

Rails 允许 render 的时候使用 :collection 参数,这样也能有效减少耗时哦!

Before

<% @replies.each do |reply| %>
  <%= render partial: 'reply', reply: reply %>
<% end %>

After:

<%= render partial: 'reply', collection: @replies %>

去掉 includes,当有 render partial cache 的时候

在这类有 partial cache 的情况下,故意 N+1 能有效减少查询开销哦。 比如 Ruby China 回帖这种情况,以前我们为了避免 N+1,所以会用 includes 方法

Controller:

def show
  @replies = @topic.replies.includes(:user)
end

View:

<%= render partial: 'reply', collection: @replies, cache: true %>

但由于页面绝大大多数下,由于 Cache 存在,已经不再需要 user 对象了,所以去掉 includes(:user) 能少掉查询用户的动作。

参考这个 Commit 7793d342bab3069221cd9cba610d0b8c864332e3

实际效果,两个动作一起处理,Ruby China 话题列表从 上一次优化 的 60ms 左右缩短到了 42ms 左右了。

很棒 前几天正好在优化自己的小 demo,发现 render partial 这边确实问题比较大,没想到现在马上就改了

Unknow user #2 May 26, 2015

:plus1:

呀,好期待耶……什么什么时候会正式出 Rails 5 啊?

嗯,期待 rails5

:plus1:

:plus1: 以前都是靠 Rails.cache + memcached 来做这种 n+1,现在方便了很多

非常棒

非常棒。👍

Nice :plus1:

这个太棒了。

昨天在 greenruby 上看到了这篇,没来得及细看,今天再看下,确实很棒

Jeff Atwood 說 https://twitter.com/codinghorror/status/602375823996235776 "we see 15ms page render times on Stack Overflow. At Discourse we are lucky to get 5x that at best"

15ms, 不知道 Ruby 什麼時候可以追上。

17 Floor has deleted

不错哗

其原理是不是把 n+1 的 cache 查询减少到 1 次查询?

def show1
  @replies = @topic.replies.includes(:user)
end

def show2
  @replies = @topic.replies
end

show1 中是为了解决 N+1 的情况。show2 中是因为页面已经做了 cache,所以不需要再 user 对象了,如果这个 show2 的东西更新频率很高,那么是不是意味着我的 cache 一直要被更换掉呢?一般这种情况有什么缓存策略来解决问题呢?

:plus1:

#20 楼 @clarkyi

【故意 N+1 能有效减少查询开销】,我觉得要看场景

如果系统 user 本身就很少 那几次 n+1 后就都 cache 了,如果 user 数量很多活跃度很随机,仍然不容易命中这种 "故意 N+1" 的 cache

怎么修改不了头像

这个对 jbuilder 里面的 partial 没效果吗 @xyuanu

不错 ya

期待 Rails 5 能在性能改善更多!!!

:plus1:

Dalli 可能对这个 multi fetch 有影响。

https://github.com/mperham/dalli/issues/106

rails 5 什么时候发布啊

如果这个 @items 里有不同的类型,不知道是否依然有效?

@huacnlee 但是 include 是不是可以减少第一次访问的时间呢?因为这时候没有 cache

我跟 @bluecoda 有一樣的疑問 不使用 includes 雖然在有 cache 的時候可以少一次 但是在第一次或是 cache expired 的時候 不是會出現 n+1 queries 的問題? 能在深入說明下嗎?

You need to Sign in before reply, if you don't have an account, please Sign up first.