1

所以我正在使用迷你分析器,它给了我一些不错的统计数据。

然而,我注意到的一件事是我已经将大量的 SQL 调用降到最低限度,现在最大的事情是各种部分和 HTML 的呈现。

例如,以下是我面临的 2 个不同的问题示例:

迷你轮廓仪

GET http://localhost:3000/   14.0   +0.0
  Executing action: index    9.5    +9.0
   Rendering: home/index     7.8    +16.0
    Rendering: home/_row     7.9    +22.0
   Rendering: layouts/application    1038.7 +32.0
    Rendering: layouts/_navigation   6.0    +586.0

开发日志

Started GET "/" for 127.0.0.1 at 2013-05-17 18:00:26 -0500
Processing by HomeController#index as HTML
  Item Load (0.6ms)  SELECT "items".* FROM "items" WHERE "items"."is_approved" = 't'
  ActsAsTaggableOn::Tag Load (0.6ms)  SELECT "tags".* FROM "tags" INNER JOIN "taggings" ON "tags"."id" = "taggings"."tag_id" WHERE "taggings"."taggable_id" = 13 AND "taggings"."taggable_type" = 'Item' AND (taggings.context = 'tags' AND taggings.tagger_id IS NULL)
  Rendered home/_row.html.erb (8.0ms)
  Rendered home/index.html.erb within layouts/application (15.7ms)
  Rendered layouts/_social_media.html.erb (0.5ms)
   (0.4ms)  SELECT items.id FROM "items" 
  ActsAsTaggableOn::Tag Load (0.7ms)  SELECT tags.*, taggings.tags_count AS count FROM "tags" JOIN (SELECT taggings.tag_id, COUNT(taggings.tag_id) AS tags_count FROM "taggings" INNER JOIN items ON items.id = taggings.taggable_id WHERE (taggings.taggable_type = 'Item' AND taggings.context = 'tags') AND (taggings.taggable_id IN(13)) GROUP BY taggings.tag_id HAVING COUNT(taggings.tag_id) > 0) AS taggings ON taggings.tag_id = tags.id ORDER BY count LIMIT 5
  Rendered layouts/_navigation.html.erb (6.1ms)
  Rendered layouts/_site_nav.html.erb (0.6ms)
  Rendered layouts/_messages.html.erb (0.2ms)
  Rendered layouts/_footer.html.erb (0.1ms)
Completed 200 OK in 1069ms (Views: 1066.0ms | ActiveRecord: 2.3ms)

顶部的 Mini-Profiler 的输出表明,主application.html.erb文件显着增加了加载时间。

这是另一个应用程序的另一个示例,其中视图是渲染时间最长的应用程序:

Started GET "/" for 127.0.0.1 at 2013-05-17 17:55:01 -0500
  User Load (0.2ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
Processing by HomeController#index as HTML
  Category Load (0.2ms)  SELECT "categories".* FROM "categories" LIMIT 6
  Banner Load (0.2ms)  SELECT "banners".* FROM "banners" INNER JOIN "banner_types" ON "banner_types"."id" = "banners"."banner_type_id" WHERE (banner_types.name = 'Featured')
  Banner Load (0.2ms)  SELECT "banners".* FROM "banners" INNER JOIN "banner_types" ON "banner_types"."id" = "banners"."banner_type_id" WHERE (banner_types.name = 'Side')
  Product Load (0.3ms)  SELECT "products".* FROM "products" 
  Vendor Load (0.3ms)  SELECT "vendors".* FROM "vendors" WHERE "vendors"."id" IN (12, 11, 10)
  Vendor Load (0.2ms)  SELECT "vendors".* FROM "vendors" 
  User Load (0.2ms)  SELECT "users".* FROM "users" 
  Rendered home/_popular_products.html.erb (16.1ms)
  Rendered home/_popular_stores.html.erb (2.4ms)
  Rendered home/index.html.erb within layouts/application (26.4ms)
  Piggybak::Sellable Load (0.2ms)  SELECT "sellables".* FROM "sellables" WHERE "sellables"."id" = 1 LIMIT 1
  Rendered layouts/_login_nav.html.erb (8.2ms)
  Rendered layouts/_navigation.html.erb (0.8ms)
  CACHE (0.0ms)  SELECT "vendors".* FROM "vendors" 
  Rendered layouts/_store_dropdown.html.erb (2.2ms)
  Rendered layouts/_header.html.erb (18.1ms)
  Rendered layouts/_messages.html.erb (0.3ms)
  Rendered layouts/_footer.html.erb (0.9ms)
Completed 200 OK in 242ms (Views: 209.8ms | ActiveRecord: 1.9ms)

诚然,这个特定的时间只是209.8ms,但它一直与5,000ms各种加载时间一样高。

如何优化这些视图和部分的渲染?或者我可以使用什么工具至少找出导致加载时间长的原因,以便我可以慢慢消除它?

4

2 回答 2

1

您正在开发中对此进行分析。请注意,由于 config.cache_classes 设置,您的应用程序会随着该环境中的每个请求重新加载。将该设置设置为 true 的分析可能会给您不同(通常更快)的结果。

在您的一条评论中,您提到了一个Vendor.all.each片段。我会检查实际需要多长时间。只需将它包裹在一个results = Benchmark.measure do块中,看看results.real会给你什么。假设它在 50 毫秒内完成。

通常,您有三个选择:

  • 如果 50 毫秒不满意,请从您以特定间隔重新生成的静态文件中读取部分渲染结果。这可以通过使用 rails 缓存机制或将其放入您使用 cronjob 重新生成的文件中来完成。无论哪种方式,这都涉及到可能在间隔之间创建的新供应商的某种延迟。

  • 或者,如果 50 毫秒适合您,您可以将它们转换为 JSON(速度很快,请查看https://github.com/ohler55/oj)并如前所述,使用 AJAX 拉取它并使用javascript。这样做的好处是 html 的实际呈现是在浏览器中完成的。浏览器的速度更快,而且通常需要处理的负载更少。

  • 如果您对 50 毫秒不满意并且您无法忍受页面上稍微过时的信息,您将不得不开始执行选项 2,但没有 ActiveRecord,因此直接使用 mysql ruby​​ 适配器。或者您确保无论何时创建新供应商或更新旧供应商,您都将 JSON 添加到相应供应商的文本列中,可能带有一些 after_validation 回调。这样你至少可以做到

    vendors = Vendor.select(:json_cache).map{|v| v.json_cache}.join(',')
    vendors = "[#{vendors}]"
    render :json => vendors
    

    ......不是很漂亮,但速度很快。

我希望,这是你想知道的。

于 2013-05-20T07:51:21.473 回答
0

在您的第二个示例中,我看到以下查询:

  • “精选”类型的所有横幅
  • “侧面”类型的所有横幅
  • 所有产品
  • 所有供应商
  • 所有用户

如果您的部分必须遍历大量对象,它将大大减慢渲染时间。

注意调用集合的迭代器,如Vendor.all.each do |v|. 永远不要在视图中调用模型类方法。在您需要的对象的控制器中构建一个集合,然后在实例变量中发送到视图。

于 2013-05-17T23:17:13.923 回答