Rails 如何降低页面 render 时的耗时以及 CPU 资源

aikko · 2013年06月26日 · 最后由 alvin2ye 回复于 2013年07月03日 · 12113 次阅读
本帖已被管理员设置为精华贴

最近在做一个项目,最后瓶颈在 cpu 上面(机器配置:16 核,32G),基本当访问页面在 200req/s 的时候,16 个 cpu 基本都被占满,无法再提高响应能力。 服务器部署的方式(nginx+passenger+redis+mysql)

passenger_max_pool_size 32;
passenger_min_instances 32;

渲染 view 的时间居然用到了 900 多毫秒

Completed 200 OK in 1443ms (Views: 937.2ms | ActiveRecord: 389.0ms)

小弟初次使用 rails,求大神帮忙分析下。

Started GET "/shows/19826" at 2013-06-26 16:50:06 +0800
Processing by ShowsController#show as HTML
Read fragment views/user_28274_medals (0.5ms)
  Rendered shared/_medals.html.erb (1.5ms)
  Rendered shows/_user.html.erb (46.8ms)
  Rendered shows/show/_operate.html.erb (1.7ms)
  Rendered posts/_zhan.html.erb (3.9ms)
Read fragment views/post_82397_comments (0.7ms)
   (9.9ms)  SELECT COUNT(*) FROM `comments` WHERE `comments`.`post_id` = 82397
  Rendered shows/_comments.html.erb (377.2ms)
  Rendered shows/_first_floor.html.erb (453.8ms)
Read fragment views/user_28274_medals (0.4ms)
  Rendered shared/_medals.html.erb (0.6ms)
  Rendered shows/_user.html.erb (2.9ms)
  Rendered shows/show/_operate.html.erb (0.5ms)
   (7.6ms)  SELECT COUNT(*) FROM `wooers` WHERE `wooers`.`show_id` = 19826
  CACHE (0.0ms)  SELECT COUNT(*) FROM `wooers` WHERE `wooers`.`show_id` = 19826
  CACHE (0.0ms)  SELECT COUNT(*) FROM `wooers` WHERE `wooers`.`show_id` = 19826
   (51.5ms)  SELECT COUNT(*) FROM `posts` WHERE `posts`.`item_id` = 14411 AND `posts`.`item_type` = 'Wooer'
  Rendered shows/_choose_form.html.erb (1.5ms)
Read fragment views/index_show_19826_wooer_14411 (0.6ms)
   (54.9ms)  SELECT COUNT(*) FROM `posts` WHERE `posts`.`item_id` = 14604 AND `posts`.`item_type` = 'Wooer'
  Rendered shows/_choose_form.html.erb (0.1ms)
Read fragment views/index_show_19826_wooer_14604 (0.5ms)
   (45.8ms)  SELECT COUNT(*) FROM `posts` WHERE `posts`.`item_id` = 14762 AND `posts`.`item_type` = 'Wooer'
  Rendered shows/_choose_form.html.erb (1.4ms)
Read fragment views/index_show_19826_wooer_14762 (0.3ms)
   (47.9ms)  SELECT COUNT(*) FROM `posts` WHERE `posts`.`item_id` = 14803 AND `posts`.`item_type` = 'Wooer'
  Rendered shows/_choose_form.html.erb (1.5ms)
Read fragment views/index_show_19826_wooer_14803 (0.5ms)
   (52.4ms)  SELECT COUNT(*) FROM `posts` WHERE `posts`.`item_id` = 14970 AND `posts`.`item_type` = 'Wooer'
  Rendered shows/_choose_form.html.erb (1.5ms)
Read fragment views/index_show_19826_wooer_14970 (0.4ms)
  Rendered shows/_wooer.html.erb (364.4ms)
  Rendered shows/_wooers.html.erb (435.4ms)
Read fragment views/post_82398_comments (0.4ms)
   (10.0ms)  SELECT COUNT(*) FROM `comments` WHERE `comments`.`post_id` = 82398
  Rendered shows/_comments.html.erb (15.3ms)
  Rendered shows/_second_floor.html.erb (487.2ms)
Read fragment views/user_645_medals (0.5ms)
  Rendered shared/_medals.html.erb (0.8ms)
  Rendered shows/_user.html.erb (5.9ms)
  Rendered shows/show/_operate.html.erb (0.6ms)
  Rendered posts/_zhan.html.erb (2.7ms)
Read fragment views/post_82401_comments (0.5ms)
   (9.8ms)  SELECT COUNT(*) FROM `comments` WHERE `comments`.`post_id` = 82401
  Rendered shows/_comments.html.erb (15.1ms)
  Rendered shows/_common_floor.html.erb (34.1ms)
Read fragment views/user_28274_medals (0.5ms)
  Rendered shared/_medals.html.erb (0.7ms)
  Rendered shows/_user.html.erb (5.4ms)
  Rendered shows/show/_operate.html.erb (0.6ms)
  Rendered posts/_zhan.html.erb (2.2ms)
Read fragment views/post_82402_comments (0.4ms)
   (9.9ms)  SELECT COUNT(*) FROM `comments` WHERE `comments`.`post_id` = 82402
  Rendered shows/_comments.html.erb (14.1ms)
  Rendered shows/_common_floor.html.erb (25.6ms)
Read fragment views/user_28274_medals (0.4ms)
  Rendered shared/_medals.html.erb (0.6ms)
  Rendered shows/_user.html.erb (4.7ms)
  Rendered shows/show/_operate.html.erb (1.0ms)
  Rendered posts/_zhan.html.erb (2.5ms)
Read fragment views/post_82403_comments (0.4ms)
   (9.9ms)  SELECT COUNT(*) FROM `comments` WHERE `comments`.`post_id` = 82403
  Rendered shows/_comments.html.erb (14.0ms)
  Rendered shows/_common_floor.html.erb (25.2ms)
Read fragment views/user_28274_medals (0.5ms)
  Rendered shared/_medals.html.erb (0.7ms)
  Rendered shows/_user.html.erb (5.1ms)
  Rendered shows/show/_operate.html.erb (0.6ms)
  Rendered posts/_zhan.html.erb (2.2ms)
Read fragment views/post_82404_comments (0.4ms)
   (9.8ms)  SELECT COUNT(*) FROM `comments` WHERE `comments`.`post_id` = 82404
  Rendered shows/_comments.html.erb (14.7ms)
  Rendered shows/_common_floor.html.erb (25.8ms)
Read fragment views/user_28274_medals (0.4ms)
  Rendered shared/_medals.html.erb (0.6ms)
  Rendered shows/_user.html.erb (4.9ms)
  Rendered shows/show/_operate.html.erb (0.7ms)
  Rendered posts/_zhan.html.erb (97.0ms)
Read fragment views/post_82405_comments (0.7ms)
   (12.7ms)  SELECT COUNT(*) FROM `comments` WHERE `comments`.`post_id` = 82405
  Rendered shows/_comments.html.erb (19.4ms)
  Rendered shows/_common_floor.html.erb (125.3ms)
Read fragment views/user_28274_medals (0.5ms)
  Rendered shared/_medals.html.erb (0.7ms)
  Rendered shows/_user.html.erb (5.8ms)
  Rendered shows/show/_operate.html.erb (0.7ms)
  Rendered posts/_zhan.html.erb (2.9ms)
Read fragment views/post_82406_comments (0.6ms)
   (10.1ms)  SELECT COUNT(*) FROM `comments` WHERE `comments`.`post_id` = 82406
  Rendered shows/_comments.html.erb (14.6ms)
  Rendered shows/_common_floor.html.erb (27.4ms)
Read fragment views/user_28274_medals (0.5ms)
  Rendered shared/_medals.html.erb (0.8ms)
  Rendered shows/_user.html.erb (5.6ms)
  Rendered shows/show/_operate.html.erb (0.6ms)
  Rendered posts/_zhan.html.erb (2.5ms)
Read fragment views/post_82407_comments (0.5ms)
   (9.8ms)  SELECT COUNT(*) FROM `comments` WHERE `comments`.`post_id` = 82407
  Rendered shows/_comments.html.erb (14.1ms)
  Rendered shows/_common_floor.html.erb (26.1ms)
Read fragment views/user_28274_medals (0.5ms)
  Rendered shared/_medals.html.erb (0.7ms)
  Rendered shows/_user.html.erb (5.0ms)
  Rendered shows/show/_operate.html.erb (0.6ms)
  Rendered posts/_zhan.html.erb (2.5ms)
Read fragment views/post_82408_comments (0.5ms)
   (10.6ms)  SELECT COUNT(*) FROM `comments` WHERE `comments`.`post_id` = 82408
  Rendered shows/_comments.html.erb (14.6ms)
  Rendered shows/_common_floor.html.erb (25.8ms)
Read fragment views/user_28274_medals (0.5ms)
  Rendered shared/_medals.html.erb (0.6ms)
  Rendered shows/_user.html.erb (5.1ms)
  Rendered shows/show/_operate.html.erb (0.6ms)
  Rendered posts/_zhan.html.erb (3.1ms)
Read fragment views/post_82409_comments (0.5ms)
   (11.0ms)  SELECT COUNT(*) FROM `comments` WHERE `comments`.`post_id` = 82409
  Rendered shows/_comments.html.erb (15.2ms)
  Rendered shows/_common_floor.html.erb (27.3ms)
  Rendered shows/_page.html.erb (5.0ms)
  Rendered shows/show.html.erb within layouts/application (1315.0ms)
  Rendered layouts/_navigation.html.erb (5.9ms)
**Completed 200 OK in 1443ms (Views: 937.2ms | ActiveRecord: 389.0ms)**


**得到各位大神的帮助,现在交优化作业,同一个页面的请求(热门帖子特例),优化效果非常明显,响应时间从 1443ms 降到 374ms,对应的日志后面有贴出。从 new relic 的统计来看,之前整站的平均响应时间为 140ms 左右,优化后降低到 80ms 左右。 刚开始使用 rails,犯了很多不该犯的错误,就当一个反面教材吧,希望能帮助像我这样的菜鸟们能更好更正确的使用 rails。

一些关键请求前后响应时间数据见下图:**

优化前:

优化后:

主要从下面几个方面进行优化,有不足的地方希望大家指出 1、该加上 counter_cache 的地方全部都加上 counter_cache 2、对常用的非主键查询加索引,例如查用户的发帖,查帖子的评论等 3、使用 cache_digests,充分 cache 住 view 中的内容 4、尽量不在 view 中设计数据库查询操作,修改页面结构减少 render 5、对于一些实时性要求不高的页面使用 action_cache(由于我的应用必须登录才能使用,action_cache 不跳过验证的)

在此感谢@nihaokid, @huacnlee, @hooopo, @kenshin54, @leonworld等各位的指点



Started GET "/shows/19826" at 2013-07-03 17:57:05 +0800
Processing by ShowsController#show as HTML
  Parameters: {"id"=>"19826"}
Read fragment views/users/28274-20130614075045/d23d462cb66fbbd1ff9b470e0ca7a163 (0.5ms)
  Rendered shows/_user.html.erb (1.1ms)
  Rendered shows/show/_operate.html.erb (0.9ms)
  Rendered posts/_zhan.html.erb (2.5ms)
Read fragment views/posts/82397-20130622231908/posts/82398-20130620091124/posts/82402-20130622231900/posts/82403-20130622232014/posts/82404-20130622232021/posts/82405-20130622232023/posts/82406-20130622232025/posts/82407-20130622232027/posts/82408-20130622232030/posts/82409-20130622232032/posts/82410-20130622232034/posts/82411-20130622232036/posts/82412-20130622232038/posts/82413-20130622232040/posts/82414-20130622232043/posts/82415-20130622232045/posts/82416-20130622232048/posts/82417-20130622232050/posts/82418-20130622232052/posts/82419-20130622232054/posts/82420-20130622232056/posts/82421-20130622232058/posts/82422-20130622232100/posts/82423-20130622232102/posts/82424-20130622232104/posts/82425-20130622232106/ee5e1083a00aad667f6151cd4224c7d8 (0.5ms)
Read fragment views/post_82397_comments/98cc0c2a9fa04cb178257dab2f371b63 (0.4ms)
  Rendered shows/_comments.html.erb (0.6ms)
  Rendered shows/_first_floor.html.erb (13.4ms)
Read fragment views/users/28274-20130614075045/d23d462cb66fbbd1ff9b470e0ca7a163 (0.5ms)
  Rendered shows/_user.html.erb (0.8ms)
Read fragment views/posts/82398-20130620091124/4797c0bc70520a2ac0bf90fc7a8e6395 (0.4ms)
   (0.8ms)  SELECT COUNT(*) FROM `posts` WHERE `posts`.`item_id` = 14762 AND `posts`.`item_type` = 'Wooer'
Read fragment views/index_show_19826_wooer_14762_info/f8384c485db4fe49a341041679e8e8d9 (0.5ms)
  Rendered shows/_choose_form.html.erb (1.4ms)
Read fragment views/index_show_19826_wooer_14762/f8384c485db4fe49a341041679e8e8d9 (0.4ms)
   (0.4ms)  SELECT COUNT(*) FROM `posts` WHERE `posts`.`item_id` = 14803 AND `posts`.`item_type` = 'Wooer'
Read fragment views/index_show_19826_wooer_14803_info/f8384c485db4fe49a341041679e8e8d9 (0.4ms)
  Rendered shows/_choose_form.html.erb (1.2ms)
Read fragment views/index_show_19826_wooer_14803/f8384c485db4fe49a341041679e8e8d9 (0.5ms)
   (0.3ms)  SELECT COUNT(*) FROM `posts` WHERE `posts`.`item_id` = 14970 AND `posts`.`item_type` = 'Wooer'
Read fragment views/index_show_19826_wooer_14970_info/f8384c485db4fe49a341041679e8e8d9 (0.5ms)
  Rendered shows/_choose_form.html.erb (1.5ms)
Read fragment views/index_show_19826_wooer_14970/f8384c485db4fe49a341041679e8e8d9 (0.5ms)
   (0.3ms)  SELECT COUNT(*) FROM `posts` WHERE `posts`.`item_id` = 17379 AND `posts`.`item_type` = 'Wooer'
Read fragment views/index_show_19826_wooer_17379_info/f8384c485db4fe49a341041679e8e8d9 (0.5ms)
  Rendered shows/_choose_form.html.erb (1.2ms)
Read fragment views/index_show_19826_wooer_17379/f8384c485db4fe49a341041679e8e8d9 (0.5ms)
   (0.5ms)  SELECT COUNT(*) FROM `posts` WHERE `posts`.`item_id` = 17622 AND `posts`.`item_type` = 'Wooer'
Read fragment views/index_show_19826_wooer_17622_info/f8384c485db4fe49a341041679e8e8d9 (0.5ms)
  Rendered shows/_choose_form.html.erb (1.1ms)
Read fragment views/index_show_19826_wooer_17622/f8384c485db4fe49a341041679e8e8d9 (0.5ms)
  Rendered shows/_wooer.html.erb (24.5ms)
  Rendered shows/_wooers.html.erb (109.0ms)
Read fragment views/post_82398_comments/98cc0c2a9fa04cb178257dab2f371b63 (0.5ms)
  Rendered shows/_comments.html.erb (0.8ms)
  Rendered shows/_second_floor.html.erb (130.3ms)
Read fragment views/users/645-20130621101340/d23d462cb66fbbd1ff9b470e0ca7a163 (0.5ms)
  Rendered shows/_user.html.erb (0.9ms)
Read fragment views/posts/82401-20130620091200/df576356e2de4bcdb9c7b0559bb5ba4c (0.4ms)
  Rendered posts/_zhan.html.erb (2.3ms)
Read fragment views/post_82401_comments/98cc0c2a9fa04cb178257dab2f371b63 (0.5ms)
  Rendered shows/_comments.html.erb (0.7ms)
  Rendered shows/_common_floor.html.erb (7.9ms)
Read fragment views/users/28274-20130614075045/d23d462cb66fbbd1ff9b470e0ca7a163 (0.6ms)
  Rendered shows/_user.html.erb (1.1ms)
Read fragment views/posts/82402-20130622231900/df576356e2de4bcdb9c7b0559bb5ba4c (0.6ms)
  Rendered posts/_zhan.html.erb (2.1ms)
Read fragment views/post_82402_comments/98cc0c2a9fa04cb178257dab2f371b63 (0.5ms)
  Rendered shows/_comments.html.erb (0.7ms)
  Rendered shows/_common_floor.html.erb (8.1ms)
Read fragment views/users/28274-20130614075045/d23d462cb66fbbd1ff9b470e0ca7a163 (0.5ms)
  Rendered shows/_user.html.erb (0.9ms)
Read fragment views/posts/82403-20130622232014/df576356e2de4bcdb9c7b0559bb5ba4c (0.5ms)
  Rendered posts/_zhan.html.erb (2.3ms)
Read fragment views/post_82403_comments/98cc0c2a9fa04cb178257dab2f371b63 (0.5ms)
  Rendered shows/_comments.html.erb (0.7ms)
  Rendered shows/_common_floor.html.erb (7.6ms)
Read fragment views/users/28274-20130614075045/d23d462cb66fbbd1ff9b470e0ca7a163 (0.5ms)
  Rendered shows/_user.html.erb (0.8ms)
Read fragment views/posts/82404-20130622232021/df576356e2de4bcdb9c7b0559bb5ba4c (0.4ms)
  Rendered posts/_zhan.html.erb (2.4ms)
Read fragment views/post_82404_comments/98cc0c2a9fa04cb178257dab2f371b63 (0.5ms)
  Rendered shows/_comments.html.erb (0.8ms)
  Rendered shows/_common_floor.html.erb (7.6ms)
Read fragment views/users/28274-20130614075045/d23d462cb66fbbd1ff9b470e0ca7a163 (0.5ms)
  Rendered shows/_user.html.erb (0.8ms)
Read fragment views/posts/82405-20130622232023/df576356e2de4bcdb9c7b0559bb5ba4c (0.4ms)
  Rendered posts/_zhan.html.erb (2.1ms)
Read fragment views/post_82405_comments/98cc0c2a9fa04cb178257dab2f371b63 (0.6ms)
  Rendered shows/_comments.html.erb (0.9ms)
  Rendered shows/_common_floor.html.erb (7.8ms)
Read fragment views/users/28274-20130614075045/d23d462cb66fbbd1ff9b470e0ca7a163 (0.5ms)
  Rendered shows/_user.html.erb (0.8ms)
Read fragment views/posts/82406-20130622232025/df576356e2de4bcdb9c7b0559bb5ba4c (0.4ms)
  Rendered posts/_zhan.html.erb (2.2ms)
Read fragment views/post_82406_comments/98cc0c2a9fa04cb178257dab2f371b63 (0.4ms)
  Rendered shows/_comments.html.erb (0.6ms)
  Rendered shows/_common_floor.html.erb (7.4ms)
Read fragment views/users/28274-20130614075045/d23d462cb66fbbd1ff9b470e0ca7a163 (0.6ms)
  Rendered shows/_user.html.erb (0.9ms)
Read fragment views/posts/82407-20130622232027/df576356e2de4bcdb9c7b0559bb5ba4c (0.5ms)
  Rendered posts/_zhan.html.erb (2.2ms)
Read fragment views/post_82407_comments/98cc0c2a9fa04cb178257dab2f371b63 (0.5ms)
  Rendered shows/_comments.html.erb (0.7ms)
  Rendered shows/_common_floor.html.erb (7.6ms)
Read fragment views/users/28274-20130614075045/d23d462cb66fbbd1ff9b470e0ca7a163 (0.5ms)
  Rendered shows/_user.html.erb (0.8ms)
Read fragment views/posts/82408-20130622232030/df576356e2de4bcdb9c7b0559bb5ba4c (0.5ms)
  Rendered posts/_zhan.html.erb (2.4ms)
Read fragment views/post_82408_comments/98cc0c2a9fa04cb178257dab2f371b63 (0.5ms)
  Rendered shows/_comments.html.erb (0.7ms)
  Rendered shows/_common_floor.html.erb (7.7ms)
Read fragment views/users/28274-20130614075045/d23d462cb66fbbd1ff9b470e0ca7a163 (0.6ms)
  Rendered shows/_user.html.erb (1.0ms)
Read fragment views/posts/82409-20130622232032/df576356e2de4bcdb9c7b0559bb5ba4c (0.4ms)
  Rendered posts/_zhan.html.erb (2.5ms)
Read fragment views/post_82409_comments/98cc0c2a9fa04cb178257dab2f371b63 (0.4ms)
  Rendered shows/_comments.html.erb (0.6ms)
  Rendered shows/_common_floor.html.erb (8.2ms)
  Rendered shows/_page.html.erb (2.8ms)
  Rendered shows/show.html.erb within layouts/application (241.6ms)
  Rendered layouts/_navigation.html.erb (5.5ms)
Completed 200 OK in 374ms (Views: 247.9ms | ActiveRecord: 2.4ms)```

是生产环境么?

Rendered shows/_first_floor.html.erb (453.8ms)

这个模板到底干了啥?

cache 没做好,太多 render 了。

#2 楼 @luikore Read fragment views/user_28274_medals (0.5ms) Rendered shared/_medals.html.erb (1.5ms) Rendered shows/_user.html.erb (46.8ms) Rendered shows/show/_operate.html.erb (1.7ms) Rendered posts/_zhan.html.erb (3.9ms) Read fragment views/post_82397_comments (0.7ms) (9.9ms) SELECT COUNT(*) FROM comments WHERE comments.post_id = 82397 Rendered shows/_comments.html.erb (377.2ms) Rendered shows/_first_floor.html.erb (453.8ms)

上面的加起来就是它啦,Rendered shows/_comments.html.erb (377.2ms) 这个耗时比较久。不过其实也就 10 条以内,因为分页了。

#3 楼 @Rei cache 我尽量做了了,由于动态数据,不好 cache

#6 楼 @aikko 哪部分是动态的,看能不能用 js 渲染,比如 time_ago 之类。

comments 不会是动态吧?

#7 楼 @Rei

<div class="comments" >
      <ul id="comments_<%=post.id%>">
        <% post.comments.last(5).each_with_index do |comment,index| %>
            <li>
              <span datetime="<%= comment.created_at %>" class="time"></span>
              <span class="num"><%= floor_num(post.comments.count,5, index) %>.</span>
              <p><strong><%= comment.user.nickname %>:</strong> <%= comment.content %></p>
            </li>
        <% end %>
      </ul>
    </div>

其实空间也不大,时间用的就是 timeago,评论显示最近 5 条,所以不能整理都 cache 住,

shows/_first_floor.html.erb 这个也很悲剧啊 这里面有什么?

#9 楼 @zj0713001 这个里面干的事情看我 5 楼的回复

#10 楼 @aikko 目前的压力还是没有问题的,我部署了 2 台机,一样的配置,可以抗住 400 reqs/s 的访问,不过很快就会抗不住了,所以再找优化方案。

#10 楼 @aikko 第一眼看成了 2 个各自 300 多和 400 多...

post.comments.last(5)这个会先取得所有的 comments 然后再取最后五条么?

#8 楼 @aikko comment.user.nickname 不会 n+1 么?

#15 楼 @zgm 呃.. n=1 的特例...

#14 楼 @luikore 这个地方不会的,对于 N+1 问题不大,因为用了 @hooopo 大神的 https://github.com/csdn-dev/second_level_cache

select count(*)太多了。建议用 counter cache。

count 查询占了大概 400ms。

#13 楼 @ywjno 会取出这个 post 的所有 comments 然后去后面 5 条,这是个问题

#18 楼 @hooopo 恩,是的,正逐步加上 counter cache,本来想着我已经在很多地方用到了 cache,所以就不会去数据库读,我理解错了。 def wooers Rails.cache.fetch("show_#{self.id}_wooers_#{self.updated_at}", expires_in: 1.hour) do self.original_wooers end end

#8 楼 @aikko 这缓存空间可大了,没看到动态的部分。

comment 片段抽成局部模板,然后用 cache_digests https://github.com/rails/cache_digests

如果这个 floor_num 能在客户端计算,就可以缓存 comment

  • 了吧?
  • #21 楼 @Rei 多谢,我研究下

    #22 楼 @luikore 主要是获取最近的 5 条,所以这些部分不能直接缓存了,不过@Rei 的方法,我研究下。

    #24 楼 @aikko 我说的粒度是单个 <li> 不是整个 <ul>

    #25 楼 @luikore 这个我原来的想法是意义不大了,不过我尝试下,测试下提升了多大的空间

    #24 楼 @aikko 把能缓存的都缓存了,让每个页面只有必要的查询(内容和评论),然后都是读片段缓存。

    comments 列表也可以缓存的,以 post 做为 key,借助 ActvieRecord 的 :touch 更新。

    SQL 部分也可以优化吧,在日志中:

    SELECT COUNT(*) FROM `posts` WHERE `posts`.`item_id` = n AND `posts`.`item_type` = 'Wooer'
    

    这条语句几乎占据了三分之二的查询时间,如果在 Item 中将 post_count 进行反范式设计(denormalized)的话,查询时间估计可以控制在 200ms 以内。

    相对于 commets,特定类别的 posts 数目的变动频率相对较小,这里应该是一个进行反范式设计的恰当时机。

    建议把数据库查询的语句放到 controller 中准备,直接用 instance variable 就好,在 erb 文件中会中断 view 生成,也会慢一些。

    item_id 和 item_type 做个联合索引吧,用空间换时间

    #29 楼 @nihaokid 这个方法不错,我试一下

    #28 楼 @xhj6 这个问题我后面加上 counter cache 应该可以解决,整个访问的 active record 的耗时会降下来,可是 render 的耗时还是比较难降下来,而且非常耗 cpu,内存,io 都不是瓶颈,主要是 cpu。

    #27 楼 @Rei 恩,主要还是 render 太耗 cpu,我想是否可以尽量降低 render 的数量来降低一些 cpu 的使用。

    #30 楼 @Peter 嗯,联合索引可以考虑,目前还不是瓶颈,当前数据量不高,且数据库用的 ssd。

    #33 楼 @aikko 如果读数据库层面已经优化得差不多,但 render 还是太慢的话,可以考虑异步加载,先显示页面主体内容给用户,然后通过 ajax 异步加载 comments 等额外部分

    #35 楼 @HungYuHei 对,这个方法不错,可以大量降低 rails 去渲染 view 的耗时。这玩意性能太差了,主框架直接输出,其它额外数据 ajax 异步加载的话,整体应该可以提升一个等级,最近也在尝试使用 sinatra 等其它框架。

    #35 楼 @HungYuHei 直接输出我测过大概可以到达 600~700 reqs/s 的访问速度吧。同样配置用 nginx+tomcat java 的话可以达到 2000 reqs/s

    passenger_min_instances 数值有点儿大,也许会带来副作用。考虑 MySQL 和 Redis 进程,也许 Passenger 的进程数为 12~14 可能会更好。

    @aikko #21 楼 @Rei 说的那个是好东西。刚看了 Rails 4 新特性的介绍,就包括了cache_digests 目测能解决你的问题。

    #39 楼 @yanhao 这个我测试过,目前 32 个算是比较适合的。

    #40 楼 @ichord 好的,我试下

    #43 楼 @Levan 我已经用了一个 second_level_cache 基本可以 cache 住很多数据库查询,对一些常用的 association 我也用了 Rails cache 这部分优化空间不大~

    #21 楼 @Rei #40 楼 @ichord 这个东西可行,至少我可以肆意的去 cache 了

    #45 楼 @aikko 把模版里所有的 current_user 和有关时间的都拿掉,放到 js 里面获取,可以最大化缓存

    非常动态的,就异步获取把,更新比不那么平凡的,fragment cache 等等做一下,数据方面可以缓存的,也丢 memcache 或 redis。就算更新稍微频繁,估一下读写比例,其实做缓存还是有用的。

    #46 楼 @knwang 我正准备能 cache 的地方都 cache 住

    #47 楼 @kenshin54 由于项目前期考虑速度都直接渲染数据,异步获取把本来需要 render 的部分丢给浏览器处理,可以很好的解决 render 的负载

    你可以换引擎,用 slim 可以减少到 1/3

    @aikko 异步是可以,但是无端多了很多并发,这个你也要考虑负载,还有服务器压力的

    #28 楼 @xhj6 对于 sql 还是先 explain 一下,目测没有加索引

    加上缓存可以把响应时间减少到 100ms 以内,你信不信?

    • stale?做 http 缓存,这个页面对应的 model 没有更新的时候,根本不需要渲染。
    • 用 rails 的 cache 来对区块进行缓存,comments 整块可以针对 model 缓存起来,也可以单独缓存 comment 以应对频繁有 comment 发出的状况。

    #50 楼 @leonworld slim 有这么快?

    #54 楼 @imlcl 先看一下瓶颈是不是在渲染上面,换模版类似于换语言,要慎重啊!

    @imlcl slim 跟 erb 我有实测数据,大概只有 1/3 的样子。 而且我用 slim 的时候也没有遇到太多问题,代码量也少很多。 而且 erb 文件跟 slim 文件在一个项目里是可以共存的。可以尝试

    #50 楼 @leonworld 如果真是这样那就最好了,我研究下,多谢

    #51 楼 @leonworld 目前大多数的 cpu 好在了 render 上面,瓶颈在 cpu,如果把 render 这块的时间和资源消耗消除或较少,应该可以大大提高并发量

    #52 楼 @nouse 是的,正在分析哪些表,哪些字段需要加索引,这样应该可以减低响应时间

    #53 楼 @linjunhalida 嘿嘿,信,我研究下·

    #55 楼 @linjunhalida 刚才看了下 slim,结构上类似于 haml,不考虑更换了,主要是前后端不是同一个人。

    #57 楼 @leonworld 嗯嗯,谢谢呢,好多人推荐 slim。我之前也看过一下 haml,不过近期可能继续用 erb

    64 楼 已删除

    #64 楼 为什么第 1 组数据差这么多?

    #66 楼 @aikko 我对这个持保留态度。 你给的数据第一次相差的那点我觉得应该是第一次编译输出时的耗时,但后面的时间你也可以看出确实是 slim 之类的快了一点。slim 官方也有说明。不知道你的用例是怎样的。在使用了部分模板的情况下不知道情况如何。在 Rails 本事的控制台输出里可以明显看出 slim 的 Render 时间要比 erb 快很多

    #67 楼 @leonworld 昨天一朋友给我截得图,就是上面那张,http://ruby-china.org/topics/634 这里面有完整版的,原文我没有找到

    #68 楼 @aikko 这个帖子我刚才也看到了 推荐你看一下下面这个链接 https://speakerdeck.com/mirakui/high-performance-rails-long-edition 里面提到了缓存 也有 slim 当然升级到 ruby2.0 也会提升一定的性能。

    拜托把 rails这个性能... 这句去掉吧,误导别人,你这么用随便换那个框架都是一样的结果

    还有别听他们说换什么 Slim 之类的,你先按照前面几楼他们给你的建议修改好,速度一下就上去了,比如 count 这个改为 counter_cache 估计一下就能减少 500ms

    #71 楼 @huacnlee 恩,已修改,的确用的很多方法非常不好,就当一个反面例子吧,我后面把根据前面楼层的建议优化后的结果放上来。多谢指点。

    换模板语言主要是为了方便而不是速度,除非这个模板特别慢,缓存起来效果一样。

    部分可以考虑 jquery template, 当然具体情况具体分析。

    页面的块级缓存建议使用 Cells 这个 gem 来做

    另外如果 render 这么慢的话,看看有没有在 erb 里写了查询数据库的代码,比如: <%= User.all %> <%= User.recent %>

    class User
      def recent
        User.where ....
      end
    end
    

    目测你的 N 个 count 不合理,要缓冲下。

    需要 登录 后方可回复, 如果你还没有账号请 注册新账号