Computer >> Máy Tính >  >> Lập trình >> Ruby

Bộ nhớ đệm Bộ sưu tập Rails

Trước đây chúng ta đã xem xét bộ nhớ đệm phân mảnh trong Rails trên AppSignal Academy. Điều này cải thiện đáng kể hiệu suất của các chế độ xem bằng cách lưu vào bộ nhớ đệm các phần nhỏ hơn của chúng. Khi bộ nhớ đệm các phần tử, chúng tôi có thêm lợi ích là có thể sử dụng lại chúng ở những nơi khác trong chế độ xem của chúng tôi với chi phí thấp.

Điều này hoạt động tốt cho các bộ sưu tập nhỏ, nhưng các vấn đề nhanh chóng phát sinh trên các bộ sưu tập lớn hơn. Trong bài viết này, chúng ta sẽ xem xét cách thức hoạt động của bộ nhớ đệm bộ sưu tập Rails và cách chúng ta có thể sử dụng nó để tăng tốc độ hiển thị của một bộ sưu tập lớn.

👋 Và nếu bạn thích bài viết này, chúng tôi đã viết nhiều hơn về hiệu suất của Ruby (trên Rails), hãy xem danh sách kiểm tra giám sát hiệu suất Ruby của chúng tôi.

Hiển thị một Bộ sưu tập

Hãy bắt đầu với một bộ điều khiển nhỏ tải 100 bài đăng cuối cùng cho trang chỉ mục của blog của chúng tôi.

class PostsController < ApplicationController
  def index
    @posts = Post.all.order(:created_at => :desc).limit(100)
  end
end

Để hiển thị các bài đăng này trong chế độ xem, chúng tôi lặp qua @posts biến phiên bản.

<!-- app/views/posts/index.html.erb -->
<h1>Posts</h1>
 
<div class="posts">
  <% @posts.each do |post| %>
    <div class="post">
      <h2><%= post.title %></h2>
      <small><%= post.author %></small>
 
      <div class="body">
        <%= post.body %>
      </div>
    </div>
  <% end %>
</div>

Khi yêu cầu trang này, chúng tôi thấy các bài đăng đang được tải xuống từ cơ sở dữ liệu và chế độ xem đang được hiển thị. Chỉ với 32 mili giây dành cho lớp xem, trang này hoạt động khá nhanh.

Started GET "/posts"
Processing by PostsController#index as HTML
  Rendering posts/index.html.erb within layouts/application
  Post Load (1.5ms)  SELECT  "posts".* FROM "posts" ORDER BY "posts"."created_at" DESC LIMIT ?  [["LIMIT", 100]]
  ↳ app/views/posts/index.html.erb:4
  Rendered posts/index.html.erb within layouts/application (19.4ms)
Completed 200 OK in 37ms (Views: 32.4ms | ActiveRecord: 2.7ms)

Kết xuất Bộ sưu tập với các phần

Tiếp theo, chúng tôi muốn sử dụng post trong một chế độ xem khác, vì vậy chúng tôi chuyển HTML của bài đăng sang một phần.

<!-- app/views/posts/index.html.erb -->
<h1>Posts</h1>
 
<div class="posts">
  <% @posts.each do |post| %>
    <%= render post %>
  <% end %>
</div>
 
<!-- app/views/posts/_post.html.erb -->
<div class="post">
  <h2><%= post.title %></h2>
  <small><%= post.author %></small>
 
  <div class="body">
    <%= post.body %>
  </div>
</div>
Started GET "/posts"
Processing by PostsController#index as HTML
  Rendering posts/index.html.erb within layouts/application
  Post Load (1.2ms)  SELECT  "posts".* FROM "posts" ORDER BY "posts"."created_at" DESC LIMIT ?  [["LIMIT", 100]]
  ↳ app/views/posts/index.html.erb:4
...
  Rendered posts/_post.html.erb (0.1ms)
  Rendered posts/_post.html.erb (0.1ms)
  Rendered posts/index.html.erb within layouts/application (205.4ms)
Completed 200 OK in 217ms (Views: 213.8ms | ActiveRecord: 1.7ms)

Với 213 mili giây dành cho lớp xem, bạn có thể thấy rằng thời gian hiển thị đã tăng lên đáng kể. Điều này là do một tệp mới (một phần) cần được tải, biên dịch và hiển thị cho mọi bài đăng. Hãy cùng xem xét cách chúng tôi có thể cải thiện thời gian hiển thị với bộ nhớ đệm phân đoạn.

Bộ nhớ đệm phân mảnh

Như được mô tả trong bài viết bộ nhớ đệm phân đoạn, chúng tôi sẽ sử dụng cache trợ giúp trong chế độ xem xung quanh render cuộc gọi. Bằng cách này, chúng tôi sẽ lưu vào bộ nhớ cache kết xuất từng phần cho mỗi bài đăng.

<!-- app/views/posts/index.html.erb -->
<h1>Posts</h1>
 
<div class="posts">
  <% @posts.each do |post| %>
    <%= cache post do %>
      <%= render post %>
    <% end %>
  <% end %>
</div>
Started GET "/posts"
Processing by PostsController#index as HTML
  Rendering posts/index_with_partial_caching.html.erb within layouts/application
  Post Load (1.4ms)  SELECT  "posts".* FROM "posts" ORDER BY "posts"."created_at" DESC LIMIT ?  [["LIMIT", 100]]
  ↳ app/views/posts/index.html.erb:4
...
Read fragment views/posts/index.1ms)
  Rendered posts/_post.html.erb (0.1ms)
Write fragment views/posts/index.1ms)
Read fragment views/posts/index.5ms)
  Rendered posts/_post.html.erb (0.1ms)
Write fragment views/posts/index.1ms)
  Rendered posts/index.html.erb within layouts/application (274.5ms)
Completed 200 OK in 286ms (Views: 281.4ms | ActiveRecord: 2.4ms)

Yêu cầu đầu tiên sẽ không nhanh hơn nhiều vì nó vẫn cần hiển thị từng phần trong lần đầu tiên và lưu trữ trong bộ nhớ cache.

Started GET "/posts"
Processing by PostsController#index as HTML
  Rendering posts/index.html.erb within layouts/application
  Post Load (2.2ms)  SELECT  "posts".* FROM "posts" ORDER BY "posts"."created_at" DESC LIMIT ?  [["LIMIT", 100]]
  ↳ app/views/posts/index.html.erb:4
...
Read fragment views/posts/index.1ms)
Read fragment views/posts/index.1ms)
  Rendered posts/index.html.erb within layouts/application (63.8ms)
Completed 200 OK in 78ms (Views: 75.5ms | ActiveRecord: 2.2ms)

Trong các yêu cầu tiếp theo, chúng tôi thấy rằng thời gian dành cho chế độ xem thấp hơn đáng kể - từ 286 mili giây xuống 78 mili giây. Tuy nhiên, nó vẫn chậm hơn rất nhiều so với những gì chúng tôi nhận được với mã gốc của mình - nó chậm gần gấp đôi.

Lưu ý:Nếu bạn không thấy dòng "Đọc / Ghi phân đoạn" trong nhật ký của mình, hãy đảm bảo bật ghi nhật ký bộ nhớ cache phân đoạn trong môi trường phát triển của bạn, được đặt thành false theo mặc định trên Rails 5.1 trở lên:

# config/environments/development.rb
config.action_controller.enable_fragment_cache_logging = true

Bộ nhớ đệm Bộ sưu tập

Trong Rails 5, rất nhiều việc đã được thực hiện để làm cho bộ nhớ đệm bộ sưu tập nhanh hơn. Để tận dụng những cải tiến này, chúng tôi sẽ cần thay đổi mã chế độ xem của mình. Thay vì gọi cache bản thân người trợ giúp, chúng tôi có thể yêu cầu Rails hiển thị toàn bộ bộ sưu tập và lưu vào bộ nhớ cache cùng một lúc.

<!-- app/views/posts/index.html.erb -->
<h1>Posts</h1>
 
<div class="posts">
  <%= render partial: :post, collection: @posts, cached: true %>
</div>

Lưu ý render @collection, cached: true viết tắt sẽ không hoạt động để cải thiện tốc độ bộ nhớ đệm này.

Started GET "/posts"
Processing by PostsController#index as HTML
  Rendering posts/index.html.erb within layouts/application
  Post Load (1.4ms)  SELECT  "posts".* FROM "posts" ORDER BY "posts"."created_at" DESC LIMIT ?  [["LIMIT", 100]]
  ↳ app/views/posts/index.html.erb:4
  Rendered collection of posts/_post.html.erb [0 / 100 cache hits] (28.2ms)
  Rendered posts/index.html.erb within layouts/application (46.6ms)
Completed 200 OK in 64ms (Views: 59.9ms | ActiveRecord: 2.0ms)

Trong yêu cầu đầu tiên, chúng tôi đã có thể thấy một sự cải thiện lớn về thời gian dành cho lớp chế độ xem. Điều này là do Rails hiện đã chuẩn bị trước, một phần được sử dụng cho toàn bộ bộ sưu tập, thay vì cho từng bài đăng riêng biệt.

Started GET "/posts"
Processing by PostsController#index as HTML
  Rendering posts/index.html.erb within layouts/application
  Post Load (1.3ms)  SELECT  "posts".* FROM "posts" ORDER BY "posts"."created_at" DESC LIMIT ?  [["LIMIT", 100]]
  ↳ app/views/posts/index.html.erb:4
  Rendered collection of posts/_post.html.erb [100 / 100 cache hits] (19.2ms)
  Rendered posts/index.html.erb within layouts/application (26.5ms)
Completed 200 OK in 37ms (Views: 35.7ms | ActiveRecord: 1.3ms)

Trong các yêu cầu tiếp theo, chúng tôi còn thấy cải thiện nhiều hơn - từ 64 mili giây xuống còn khoảng 35 mili giây. Một cải tiến lớn về tốc độ cho toàn bộ bộ sưu tập được thực hiện ở đây bằng cách tối ưu hóa Rails cho các bộ sưu tập. Thay vì kiểm tra tính khả dụng của bộ đệm cho từng phần, Rails kiểm tra tất cả các khóa bộ đệm của bộ sưu tập cùng một lúc, tiết kiệm thời gian truy vấn kho lưu trữ bộ đệm.

Một lợi ích bổ sung của trình trợ giúp bộ nhớ đệm này là ghi nhật ký tóm tắt của bộ sưu tập. Trong yêu cầu đầu tiên, không tìm thấy khóa nào trong số các khóa bộ nhớ cache [0 / 100 cache hits] , nhưng trong yêu cầu thứ hai, tất cả chúng đều được tìm thấy [100 / 100 cache hits] .

Sau khi cập nhật một số đối tượng trong cơ sở dữ liệu, chúng tôi thậm chí có thể xem có bao nhiêu khóa đã cũ.

Rendered collection of posts/_post.html.erb [88 / 100 cache hits] (13.4ms)

Có nhiều cải thiện tốc độ cần đạt được với bộ nhớ đệm và hiển thị bộ sưu tập được tối ưu hóa này. Sự khác biệt thậm chí còn lớn hơn sẽ được tạo ra khi hiển thị các bộ sưu tập lớn hơn. Trừ khi bạn cần các chế độ xem tùy chỉnh cho bộ sưu tập của mình, chiến lược được tối ưu hóa này là cách để áp dụng cho các ứng dụng Rails của bạn. Tại AppSignal, chúng tôi đã quản lý để tăng tốc đáng kể một trong các chế độ xem quản trị của chúng tôi đang hiển thị hàng nghìn bản ghi, theo cách này.

Bạn có bất kỳ câu hỏi nào về bộ sưu tập bộ nhớ đệm trong Rails? Vui lòng cho chúng tôi biết tại @AppSignal! Nếu bạn có bất kỳ nhận xét nào về bài viết hoặc nếu bạn có bất kỳ chủ đề nào mà bạn muốn chúng tôi đề cập, vui lòng liên hệ với chúng tôi.