Ruby on rails 如何优化HTML而不是SQL查询的呈现?
所以我使用迷你档案器,它给了我一些很好的统计数据 然而,我注意到的一件事是,我已经将许多SQL调用降到最低,现在最大的事情是呈现各种部分和HTML 例如,以下是我面临的两个不同问题的例子: 迷你档案器Ruby on rails 如何优化HTML而不是SQL查询的呈现?,ruby-on-rails,ruby-on-rails-3,performance,optimization,mvc-mini-profiler,Ruby On Rails,Ruby On Rails 3,Performance,Optimization,Mvc Mini Profiler,所以我使用迷你档案器,它给了我一些很好的统计数据 然而,我注意到的一件事是,我已经将许多SQL调用降到最低,现在最大的事情是呈现各种部分和HTML 例如,以下是我面临的两个不同问题的例子: 迷你档案器 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
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的输出显示,mainapplication.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
,但在不同的加载时间,这一时间高达5000ms
如何优化这些视图和部分的渲染?或者,我可以使用什么工具来至少找出导致加载时间过长的原因,以便我可以缓慢地进行处理?在第二个示例中,我看到了以下查询:
- 所有类型为“特色”的横幅
- 所有类型为“侧面”的横幅
- 所有产品
- 所有供应商
- 所有用户
注意调用集合的迭代器,如
Vendor.all.each do | v |
。不应在视图中调用模型类方法。在控制器中构建所需对象的集合,然后在实例变量中发送到视图。在第二个示例中,我看到了以下查询:
- 所有类型为“特色”的横幅
- 所有类型为“侧面”的横幅
- 所有产品
- 所有供应商
- 所有用户
注意调用集合的迭代器,如
Vendor.all.each do | v |
。不应在视图中调用模型类方法。在控制器中构建所需对象的集合,然后在实例变量中发送到视图。您正在开发中对此进行分析。请注意,由于config.cache_classes设置,应用程序将随该环境中的每个请求一起重新加载。将该设置设置为true的评测可能会得到不同(通常更快)的结果
在您的一条评论中,您提到了一个供应商.all.each
代码片段。我会检查实际需要多长时间。只需将其包装在一个results=Benchmark.measure do
块中,然后查看results.real
为您提供了什么。假设它在50毫秒内完成
通常,您有三种选择:
- 如果50ms不符合要求,请从以特定间隔重新生成的静态文件中读取部分渲染结果。这可以通过使用rails缓存机制或将其放入使用cronjob重新生成的文件中来实现。无论哪种方式,这都会对间隔之间可能创建的新供应商造成某种延迟
- 或者,如果50ms适合您,您可以将它们转换为JSON(速度很快,请看一看),然后按照前面的建议,使用AJAX将其拉出来,并使用javascript呈现html。这样做的优点是html的实际呈现是在浏览器中完成的。浏览器的速度更快,而且通常需要处理的负载更少
- 如果你对50ms不满意,并且你不能忍受页面上稍微过时的信息,那么你必须开始做选项2,但是没有ActiveRecord,所以直接使用mysql ruby适配器。或者确保无论何时创建新供应商或更新旧供应商,都会将JSON添加到相应供应商的文本列中,可能会有一些事后验证回调。这样你至少可以做到
。。。不太漂亮,但燃烧得很快vendors = Vendor.select(:json_cache).map{|v| v.json_cache}.join(',') vendors = "[#{vendors}]" render :json => vendors
我希望,这是您想要知道的。您正在开发中分析这一点。请注意,由于config.cache_classes设置,应用程序将随该环境中的每个请求一起重新加载。将该设置设置为true的评测可能会得到不同(通常更快)的结果 在您的一条评论中,您提到了一个
供应商.all.each
代码片段。我会检查实际需要多长时间。只需将其包装在一个results=Benchmark.measure do
块中,然后查看results.real
为您提供了什么。假设它在50毫秒内完成
通常,您有三种选择:
- 如果50ms不符合要求,请从以特定间隔重新生成的静态文件中读取部分渲染结果。这可以通过使用rails缓存机制或将其放入使用cronjob重新生成的文件中来实现。无论哪种方式,这都会对间隔之间可能创建的新供应商造成某种延迟
- 或者,如果50ms适合您,您可以将它们转换为JSON(速度很快,请看一看),然后按照前面的建议,使用AJAX将其拉出来,并使用javascript呈现html。这样做的优点是html的实际呈现是在浏览器中完成的。浏览器的速度更快,而且通常需要处理的负载更少
- 如果你对50ms不满意,并且你不能忍受页面上稍微过时的信息,那么你必须开始做选项2,但是没有ActiveRecord,所以直接使用mysql ruby适配器。或者确保无论何时创建新供应商或更新旧供应商,都会将JSON添加到相应供应商的文本列中,可能会有一些事后验证回调。这样你至少可以做到
。。。不太漂亮,但燃烧得很快vendors = Vendor.select(:json_cache).map{|v| v.json_cache}.join(',') vendors = "[#{vendors}]" render :json => vendors