Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

แคชคอลเลกชัน Rails

ก่อนหน้านี้เราได้ดูการแคชส่วนย่อยใน Rails บน AppSignal Academy วิธีนี้ช่วยปรับปรุงประสิทธิภาพของการดูได้อย่างมากด้วยการแคชส่วนที่มีขนาดเล็กลง เมื่อแคชบางส่วน เรามีประโยชน์เพิ่มเติมในการนำกลับมาใช้ใหม่ในส่วนอื่นในมุมมองของเราโดยเสียค่าใช้จ่ายเพียงเล็กน้อย

วิธีนี้ใช้ได้ผลดีกับคอลเล็กชันขนาดเล็ก แต่ปัญหาเกิดขึ้นอย่างรวดเร็วในคอลเล็กชันขนาดใหญ่ ในบทความนี้ เราจะมาดูกันว่าการแคชคอลเล็กชัน Rails ทำงานอย่างไร และเราจะใช้เพื่อเร่งการแสดงผลคอลเล็กชันขนาดใหญ่ได้อย่างไร

👋 และหากคุณชอบบทความนี้ ยังมีอีกมากที่เราเขียนเกี่ยวกับประสิทธิภาพของ Ruby (on Rails) ให้ดูที่รายการตรวจสอบการตรวจสอบประสิทธิภาพของ Ruby

การแสดงผลคอลเล็กชัน

เริ่มต้นด้วยตัวควบคุมขนาดเล็กที่โหลด 100 โพสต์ล่าสุดสำหรับหน้าดัชนีของบล็อกของเรา

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

หากต้องการแสดงโพสต์เหล่านี้ในมุมมอง เราจะวนซ้ำ @posts ตัวแปรอินสแตนซ์

<!-- 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>

เมื่อขอหน้านี้ เราจะเห็นการดึงข้อมูลโพสต์จากฐานข้อมูลและมุมมองที่กำลังแสดงผล หน้านี้ใช้เวลาเพียง 32 มิลลิวินาทีในเลเยอร์การดู หน้านี้ค่อนข้างเร็ว

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)

การแสดงผลคอลเลกชันที่มีบางส่วน

ต่อไปเราต้องการใช้ post องค์ประกอบในอีกมุมมองหนึ่ง ดังนั้นเราจึงย้ายโพสต์ HTML เป็นบางส่วน

<!-- 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)

ด้วยเวลา 213 มิลลิวินาทีที่ใช้ไปกับเลเยอร์การดู คุณจะเห็นว่าเวลาการเรนเดอร์เพิ่มขึ้นอย่างมาก เนื่องจากจำเป็นต้องโหลด รวบรวม และแสดงผลไฟล์ใหม่ (บางส่วน) สำหรับทุกโพสต์ มาดูกันสั้น ๆ ว่าเราจะสามารถปรับปรุงเวลาในการแสดงผลด้วยการแคชส่วนย่อยได้อย่างไร

การแคชแฟรกเมนต์

ตามที่อธิบายไว้ในบทความการแคชแฟรกเมนต์ เราจะใช้ cache ผู้ช่วยในมุมมองรอบ ๆ render เรียก. ด้วยวิธีนี้ เราจะแคชการแสดงผลบางส่วนสำหรับทุกโพสต์

<!-- 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)

คำขอแรกจะไม่เร็วขึ้นมากนัก เนื่องจากยังคงต้องแสดงผลทุกส่วนในครั้งแรกและเก็บไว้ในที่เก็บแคช

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)

ในคำขอที่ตามมา เราเห็นว่าเวลาที่ใช้ในการดูลดลงมาก - จาก 286 มิลลิวินาทีเหลือ 78 มิลลิวินาที ถึงกระนั้นก็ยังช้ากว่าโค้ดเดิมที่เราได้รับมาก ซึ่งช้ากว่าเกือบสองเท่า

หมายเหตุ:หากคุณไม่เห็นบรรทัด "อ่าน/เขียนแฟรกเมนต์" ในบันทึกของคุณ อย่าลืมเปิดใช้งานการบันทึกแคชของแฟรกเมนต์ในสภาพแวดล้อมการพัฒนาของคุณ ซึ่งตั้งค่าเป็น false โดยค่าเริ่มต้นใน Rails 5.1 ขึ้นไป:

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

แคชคอลเลคชัน

ใน Rails 5 มีการทำงานมากมายเพื่อทำให้การแคชคอลเลกชันเร็วขึ้น เพื่อใช้ประโยชน์จากการปรับปรุงเหล่านี้ เราจะต้องเปลี่ยนโค้ดการดูของเรา แทนที่จะเรียก cache ตัวช่วยเอง เราสามารถขอให้ Rails แสดงคอลเลกชันทั้งหมดและแคชพร้อมกันได้

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

หมายเหตุ render @collection, cached: true ชวเลขจะไม่ทำงานสำหรับการปรับปรุงความเร็วแคชนี้

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)

ในคำขอแรก เราสามารถเห็นการปรับปรุงครั้งใหญ่ของเวลาที่ใช้ไปกับเลเยอร์การดู เนื่องจากตอนนี้ Rails ได้เตรียมการไว้ล่วงหน้า บางส่วนถูกใช้สำหรับคอลเลกชันทั้งหมด แทนที่จะใช้สำหรับแต่ละโพสต์แยกกัน

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)

ในคำขอที่ตามมา เราเห็นการปรับปรุงมากขึ้น - จาก 64 มิลลิวินาทีลงไปประมาณ 35 มิลลิวินาที การปรับปรุงความเร็วครั้งใหญ่สำหรับคอลเล็กชันทั้งหมดทำได้ที่นี่โดยการเพิ่มประสิทธิภาพ Rails สำหรับคอลเล็กชัน แทนที่จะตรวจสอบความพร้อมใช้งานของแคชสำหรับทุกส่วน Rails จะตรวจสอบคีย์แคชทั้งหมดของคอลเล็กชันพร้อมกัน ซึ่งช่วยประหยัดเวลาในการสืบค้นที่จัดเก็บแคช

ประโยชน์เพิ่มเติมของตัวช่วยแคชนี้คือการบันทึกสรุปของคอลเล็กชัน ในคำขอแรก ไม่พบคีย์แคช [0 / 100 cache hits] แต่ในคำขอที่ 2 พบทั้งหมด [100 / 100 cache hits] .

หลังจากอัปเดตออบเจ็กต์บางรายการในฐานข้อมูลแล้ว เราจะเห็นได้ว่าคีย์ที่ค้างอยู่กี่คีย์

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

มีการปรับปรุงความเร็วมากมายที่จะได้รับด้วยการแสดงผลและการแคชคอลเลกชันที่ปรับให้เหมาะสมนี้ ความแตกต่างที่ยิ่งใหญ่กว่าจะเกิดขึ้นเมื่อแสดงคอลเล็กชันที่ใหญ่ขึ้น เว้นแต่ว่าคุณต้องการมุมมองที่ปรับแต่งเองสำหรับคอลเล็กชันของคุณ กลยุทธ์ที่ปรับให้เหมาะสมนี้เป็นวิธีที่จะไปสำหรับแอป Rails ของคุณ ที่ AppSignal เราจัดการเพิ่มความเร็วในการดูหนึ่งในผู้ดูแลระบบของเราได้อย่างมาก ซึ่งแสดงบันทึกเป็นพันๆ รายการด้วยวิธีนี้

มีคำถามเกี่ยวกับการแคชคอลเลกชันใน Rails หรือไม่? โปรดอย่าลังเลที่จะแจ้งให้เราทราบที่ @AppSignal! หากคุณมีความคิดเห็นเกี่ยวกับบทความหรือมีหัวข้อที่ต้องการให้เราพูดถึง โปรดติดต่อเรา