Ruby on rails 我的索引页加载缓慢。如何减少加载时间?

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:

我已经编写了一个简单的rails应用程序,允许用户阅读和共享文章。加载索引页需要相当长的时间(15秒以上)。该应用程序使用活动存储来存储图像。您可以在下面的剪报中看到相关逻辑:

文章范本:

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调用的加载速度都将比在开发环境中快得多。也许把它放到一个隐藏的网页上,这样你就可以看到它在网络中的表现