Ruby on rails 我的索引页加载缓慢。如何减少加载时间?
我已经编写了一个简单的rails应用程序,允许用户阅读和共享文章。加载索引页需要相当长的时间(15秒以上)。该应用程序使用活动存储来存储图像。您可以在下面的剪报中看到相关逻辑: 文章范本:Ruby on rails 我的索引页加载缓慢。如何减少加载时间?,ruby-on-rails,Ruby On Rails,我已经编写了一个简单的rails应用程序,允许用户阅读和共享文章。加载索引页需要相当长的时间(15秒以上)。该应用程序使用活动存储来存储图像。您可以在下面的剪报中看到相关逻辑: 文章范本: class Article < ApplicationRecord belongs_to :user has_one_attached :image has_many :votes validates :title, presence: true, length: { maximum:
class Article < ApplicationRecord
belongs_to :user
has_one_attached :image
has_many :votes
validates :title, presence: true, length: { maximum: 50 }
validates :text, presence: true
scope :news, -> { where(category: 'news') }
scope :business, -> { where(category: 'business') }
scope :ent, -> { where(category: 'entertainment') }
scope :tech, -> { where(category: 'tech') }
scope :sports, -> { where(category: 'sports') }
scope :op, -> { where(category: 'opinion') }
end
索引视图:
<div class="grid-container">
<div class="main" style="background-image: url(<%= (url_for(@articles.last.image)) %>)">
<div class="top-article" >
<h2><%= @articles.last.title %></h2>
<p><%= link_to "Read more..." %></p>
</div>
</div>
<div class="sports" style="background-image: url(<%= (url_for(@articles.sports.last.image)) %>)">
<h2><%= link_to "Sports", sports_path %></h2>
</div>
<div class="tech" style="background-image: url(<%= (url_for(@articles.tech.last.image)) %>)">
<h2><%= link_to "Technology", tech_path %></h2>
</div>
<div class="opinion" style="background-image: url(<%= (url_for(@articles.op.last.image)) %>)">
<h2><%= link_to "Opinion", opinion_path %></h2>
</div>
<div class="entertainment" style="background-image: url(<%= (url_for(@articles.ent.last.image)) %>)">
<h2><%= link_to "Entertainment", entertainment_path %></h2>
</div>
<div class="business" style="background-image: url(<%= (url_for(@articles.business.last.image)) %>)">
<h2><%= link_to "Business", business_path %></h2>
</div>
<div class="news" style="background-image: url(<%= (url_for(@articles.news.last.image)) %>)">
<h2><%= link_to "News", news_path %></h2>
</div>
</div>
我怎样才能减少这个页面的加载时间呢?rails日志是一个非常有用但经常被忽略的东西。一开始它看起来像胡言乱语,所以让我们通过它来看看经济放缓;在ArticlesController进行
处理后,索引为HTML
,开始呈现索引页面
它做的第一件事是:
Article Load (149.4ms) SELECT "articles".* FROM "articles" ORDER BY "articles"."id" DESC LIMIT $1 [["LIMIT", 1]]
↳ app/views/articles/index.html.erb:2
显示正在调用的DB查询(获取最后一篇文章)、需要多长时间(149ms)以及由于index.html.erb第2行的代码需要它。在第2行,您有@articles.last.image
——这导致了DB查询
149毫秒,没什么大不了的…跟我在一起,这是一段旅程
接下来它会做另一个2db查询,也是由第2行引起的
ActiveStorage::Attachment Load (102.9ms) SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_type" = $1 AND "active_storage_attachments"."name" = $2 AND "active_storage_attachments"."record_id" = $3 [["record_type", "Article"], ["name", "image"], ["record_id", 8]]
↳ app/views/articles/index.html.erb:2
ActiveStorage::Blob Load (33.3ms) SELECT "active_storage_blobs".* FROM "active_storage_blobs" WHERE "active_storage_blobs"."id" = $1 [["id", 7]]
↳ app/views/articles/index.html.erb:2
这一次,它获得了图像资源,这需要额外的102ms+33ms。但是,您的控制器使用的是模型。如果附加了图像
来预加载图像,是否?嗯,很明显,这不起作用。请查看有关预加载此文件的良好指南
我想你把都放错地方了。您应该使用Model.with_attached_image.all
而不是Model.all.with_attached_image
但是,还有更多
接下来,视图中的第4行需要3个DB查询:
CACHE Article Load (0.0ms) SELECT "articles".* FROM "articles" ORDER BY "articles"."id" DESC LIMIT $1 [["LIMIT", 1]]
↳ app/views/articles/index.html.erb:4
CACHE ActiveStorage::Attachment Load (0.0ms) SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_type" = $1 AND "active_storage_attachments"."name" = $2 AND "active_storage_attachments"."record_id" = $3 [["record_type", "Article"], ["name", "image"], ["record_id", 8]]
↳ app/views/articles/index.html.erb:4
CACHE ActiveStorage::Blob Load (0.0ms) SELECT "active_storage_blobs".* FROM "active_storage_blobs" WHERE "active_storage_blobs"."id" = $1 [["id", 7]]
↳ app/views/articles/index.html.erb:4
但是,请注意这些都需要0.0毫秒,并且是如何缓存的。这是因为前面的查询(由第2行引起)已经从数据库中获取了该数据。好极了
在开发过程中,请留意日志中的这些详细信息。注意,当行以缓存开始时,这很好
然后,第8、11、14、17和20行将导致15个未缓存的DB查询,在这些查询中,您使用类别作用域来获取数据,而无需预加载图像数据(@articles.business.last.image
)。因此,每个作用域调用进行3次DB查询
Article Load (0.3ms) SELECT "articles".* FROM "articles" WHERE "articles"."category" = $1 ORDER BY "articles"."id" DESC LIMIT $2 [["category", "sports"], ["LIMIT", 1]]
↳ app/views/articles/index.html.erb:8
ActiveStorage::Attachment Load (0.5ms) SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_type" = $1 AND "active_storage_attachments"."name" = $2 AND "active_storage_attachments"."record_id" = $3 [["record_type", "Article"], ["name", "image"], ["record_id", 6]]
↳ app/views/articles/index.html.erb:8
ActiveStorage::Blob Load (0.3ms) SELECT "active_storage_blobs".* FROM "active_storage_blobs" WHERE "active_storage_blobs"."id" = $1 [["id", 5]]
↳ app/views/articles/index.html.erb:8
希望这已经向您展示了如何在日志中发现时间的去向
要开始修复它,从小事做起。调用一次以获取最后一篇文章的数据和图像
控制器
@latest_article = Articles.with_attached_image.last
看法
这样,每个类别将有一个DB调用—远少于15个
但是我注意到你只展示了这些图片,而不是标题。这只是为了简短起见吗
理想情况下,您希望将这一切降低到仅1db查询;但这需要一个嵌套的SQL查询;这甚至可以证明它自己的问题
生产部署说明
在生产环境中,对映像的所有GET调用的加载速度都将比在开发环境中快得多。也许把它放到一个隐藏的页面上,这样你就可以看到它在生产中的表现——一旦你把DB查询降到1或2
在开发过程中,您可以从一个服务器线程提供所有服务;所以每件事都一个接一个地发生。在生产过程中,对映像的请求(日志中的所有GET请求,而不是索引的第一个GET请求)将同时进行。这将对您看到的15秒加载时间产生巨大影响 rails日志非常有用,但经常被忽略。一开始它看起来像胡言乱语,所以让我们通过它来看看经济放缓;在ArticlesController进行处理后,索引为HTML
,开始呈现索引页面
它做的第一件事是:
Article Load (149.4ms) SELECT "articles".* FROM "articles" ORDER BY "articles"."id" DESC LIMIT $1 [["LIMIT", 1]]
↳ app/views/articles/index.html.erb:2
显示正在调用的DB查询(获取最后一篇文章)、需要多长时间(149ms)以及由于index.html.erb第2行的代码需要它。在第2行,您有@articles.last.image
——这导致了DB查询
149毫秒,没什么大不了的…跟我在一起,这是一段旅程
接下来它会做另一个2db查询,也是由第2行引起的
ActiveStorage::Attachment Load (102.9ms) SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_type" = $1 AND "active_storage_attachments"."name" = $2 AND "active_storage_attachments"."record_id" = $3 [["record_type", "Article"], ["name", "image"], ["record_id", 8]]
↳ app/views/articles/index.html.erb:2
ActiveStorage::Blob Load (33.3ms) SELECT "active_storage_blobs".* FROM "active_storage_blobs" WHERE "active_storage_blobs"."id" = $1 [["id", 7]]
↳ app/views/articles/index.html.erb:2
这一次,它获得了图像资源,这需要额外的102ms+33ms。但是,您的控制器使用的是模型。如果附加了图像
来预加载图像,是否?嗯,很明显,这不起作用。请查看有关预加载此文件的良好指南
我想你把都放错地方了。您应该使用Model.with_attached_image.all
而不是Model.all.with_attached_image
但是,还有更多
接下来,视图中的第4行需要3个DB查询:
CACHE Article Load (0.0ms) SELECT "articles".* FROM "articles" ORDER BY "articles"."id" DESC LIMIT $1 [["LIMIT", 1]]
↳ app/views/articles/index.html.erb:4
CACHE ActiveStorage::Attachment Load (0.0ms) SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_type" = $1 AND "active_storage_attachments"."name" = $2 AND "active_storage_attachments"."record_id" = $3 [["record_type", "Article"], ["name", "image"], ["record_id", 8]]
↳ app/views/articles/index.html.erb:4
CACHE ActiveStorage::Blob Load (0.0ms) SELECT "active_storage_blobs".* FROM "active_storage_blobs" WHERE "active_storage_blobs"."id" = $1 [["id", 7]]
↳ app/views/articles/index.html.erb:4
但是,请注意这些都需要0.0毫秒,并且是如何缓存的。这是因为前面的查询(由第2行引起)已经从数据库中获取了该数据。好极了
在开发过程中,请留意日志中的这些详细信息。注意,当行以缓存开始时,这很好
然后,第8、11、14、17和20行将导致15个未缓存的DB查询,在这些查询中,您使用类别作用域来获取数据,而无需预加载图像数据(@articles.business.last.image
)。因此,每个作用域调用进行3次DB查询
Article Load (0.3ms) SELECT "articles".* FROM "articles" WHERE "articles"."category" = $1 ORDER BY "articles"."id" DESC LIMIT $2 [["category", "sports"], ["LIMIT", 1]]
↳ app/views/articles/index.html.erb:8
ActiveStorage::Attachment Load (0.5ms) SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_type" = $1 AND "active_storage_attachments"."name" = $2 AND "active_storage_attachments"."record_id" = $3 [["record_type", "Article"], ["name", "image"], ["record_id", 6]]
↳ app/views/articles/index.html.erb:8
ActiveStorage::Blob Load (0.3ms) SELECT "active_storage_blobs".* FROM "active_storage_blobs" WHERE "active_storage_blobs"."id" = $1 [["id", 5]]
↳ app/views/articles/index.html.erb:8
希望这已经向您展示了如何在日志中发现时间的去向
要开始修复它,从小事做起。调用一次以获取最后一篇文章的数据和图像
控制器
@latest_article = Articles.with_attached_image.last
看法
这样,每个类别将有一个DB调用—远少于15个
但是我注意到你只展示了这些图片,而不是标题。这只是为了简短起见吗
理想情况下,您希望将这一切降低到仅1db查询;但这需要一个嵌套的SQL查询;这甚至可以证明它自己的问题
生产部署说明
在生产环境中,对映像的所有GET调用的加载速度都将比在开发环境中快得多。也许把它放到一个隐藏的网页上,这样你就可以看到它在网络中的表现