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

Rails นั้นรวดเร็ว:เพิ่มประสิทธิภาพการดูของคุณ

ในโพสต์นี้ เราจะพิจารณาวิธีการปรับปรุงประสิทธิภาพการดู Rails ที่ผ่านการทดลองและเป็นจริง โดยเฉพาะอย่างยิ่ง ฉันจะเน้นที่ประสิทธิภาพของฐานข้อมูล การจัดการมุมมอง และการแคช

ฉันคิดว่าวลี "การเพิ่มประสิทธิภาพก่อนวัยอันควรเป็นรากเหง้าของความชั่วร้ายทั้งหมด" ถูกนำออกจากบริบทเล็กน้อย ฉันมักจะได้ยินนักพัฒนาใช้สิ่งนี้ในระหว่างการตรวจสอบโค้ดเมื่อมีการชี้ให้เห็นถึงเทคนิคการเพิ่มประสิทธิภาพอย่างง่าย คุณรู้จักผู้มีชื่อเสียง "ฉันจะทำให้มันทำงานแล้วเพิ่มประสิทธิภาพ" - จากนั้นทดสอบ - จากนั้นดีบั๊ก - จากนั้นทดสอบอีกครั้ง และอื่นๆ!

โชคดีที่มีเทคนิคด้านประสิทธิภาพและการปรับให้เหมาะสมที่ง่ายและมีประสิทธิภาพที่คุณสามารถใช้ได้ตั้งแต่เริ่มเขียนโค้ด

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

ตลอดทั้งโพสต์ เราจะใช้แอป Rails พื้นฐาน ทำการปรับปรุงและเปรียบเทียบผลลัพธ์

แอป Rails พื้นฐานมีรุ่นต่อไปนี้:

  • บุคคล (มีหลายที่อยู่)

    • ชื่อ:สตริง
    • votes_count:integer
  • โปรไฟล์ (เป็นของบุคคล)

    • ที่อยู่:สตริง

นี่คือลักษณะโมเดลบุคคลของเรา:

# == Schema Information
#
# Table name: people
#
#  id          :integer          not null, primary key
#  name        :string
#  votes_count :integer
#  created_at  :datetime         not null
#  updated_at  :datetime         not null
#
 
class Person < ApplicationRecord
  # Relationships
  has_many :profiles
 
  # Validations
  validates_presence_of :name
  validates_uniqueness_of :name
 
  def vote!
    update votes_count: votes_count + 1
  end
end

นี่คือรหัสสำหรับรูปแบบโปรไฟล์ของเรา:

# == Schema Information
#
# Table name: profiles
#
#  id         :integer          not null, primary key
#  address    :text
#  person_id  :integer
#  created_at :datetime         not null
#  updated_at :datetime         not null
#
 
class Profile < ApplicationRecord
  # Relationships
  belongs_to :person
 
  # Validations
  validates_presence_of :address
end
 

นอกจากนี้ยังมีไฟล์เมล็ดพันธุ์สำหรับบรรจุคน 1,000 คน เราสามารถทำสิ่งนี้ได้อย่างง่ายดายโดยใช้ Faker gem

ตอนนี้เรากำลังจะสร้างการกระทำที่เรียกว่า "home" ใน ApplicationController

def home
  @people = Person.all
end

รหัสสำหรับ home.html.erb ของเรามีดังนี้:

<ul>
  <% @people.each do |person| %>
    <li id="<%= person.id %>"><%= render person %></li>
  <% end %>
</ul>

ลองทำแบบแห้งและวัดประสิทธิภาพของหน้าเว็บของเราเทียบกับสิ่งนี้

หน้านั้นใช้เวลาในการโหลดมากถึง 1066.7ms ไม่ดี! นี่คือสิ่งที่เราจะตั้งเป้าที่จะลด

แบบสอบถามฐานข้อมูล

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

เพื่อการสาธิตนี้ ฉันจะใช้ฐานข้อมูล MySQL

มาดูกันว่าการโหลดเริ่มต้น 1066ms นั้นพังอย่างไร

414.7 เพื่อดำเนินการ 'controllers/application_controller#home'

...
(0.1ms)  SELECT "profiles"."address" FROM "profiles" WHERE "profiles"."person_id" = ?  [["person_id", 996]]
Rendered people/_person.html.erb (1.5ms)
(0.2ms)  SELECT "profiles"."address" FROM "profiles" WHERE "profiles"."person_id" = ?  [["person_id", 997]]
Rendered people/_person.html.erb (2.3ms)
(0.1ms)  SELECT "profiles"."address" FROM "profiles" WHERE "profiles"."person_id" = ?  [["person_id", 998]]
Rendered people/_person.html.erb (2.1ms)
(0.2ms)  SELECT "profiles"."address" FROM "profiles" WHERE "profiles"."person_id" = ?  [["person_id", 999]]
Rendered people/_person.html.erb (2.3ms)
(0.2ms)  SELECT "profiles"."address" FROM "profiles" WHERE "profiles"."person_id" = ?  [["person_id", 1000]]
Rendered people/_person.html.erb (2.0ms)
Rendered application/home.html.erb within layouts/application (890.5ms)

Completed 200 OK in 1066ms (Views: 890.5ms | ActiveRecord: 175.4ms)

519.2 และ 132.8 เพื่อแสดง "application/home.html.erb" และ "people/_person.html.erb" บางส่วน

คุณสังเกตเห็นอะไรแปลก ๆ ไหม?

เราทำการเรียกฐานข้อมูลหนึ่งครั้งในคอนโทรลเลอร์ แต่ทุก ๆ ส่วนของมันก็ทำการเรียกฐานข้อมูลของตัวเองเช่นกัน! ขอแนะนำปัญหาการสืบค้น N+1

1. แบบสอบถาม N+1

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

มาดูกันว่า "people/_person.html.erb" ทำอะไรได้บ้าง:

<ul>
  <li>
    Name: <%= person.name %>
  </li>
  <li>
    Addresses:
      <ul>
        <% person.profiles.each do |profile| %>
          <li><%= profile.address %></li>
        <% end %>
      </ul>
  </li>
</ul>
 
<%= button_to "Vote #{person.votes_count}", vote_person_path(person) %>

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

ในการเพิ่มประสิทธิภาพนี้ ให้ใช้ประโยชน์จากการรวมฐานข้อมูล MySQL และ Rails ActiveRecord มีฟังก์ชัน

มาเปลี่ยนคอนโทรลเลอร์ให้ตรงกันดังต่อไปนี้:

def home
  @people = Person.all.includes(:profiles)
end

ผู้คนทั้งหมดถูกโหลดโดย 1 แบบสอบถาม MySQL และแบบสอบถามที่เกี่ยวข้องทั้งหมดจะถูกโหลดในอีกอันหนึ่ง นำ N+1 มาสู่ 2 คำค้นหาเท่านั้น

มาดูกันว่าสิ่งนี้ช่วยเพิ่มประสิทธิภาพได้อย่างไร!

เราใช้เวลาเพียง 936ms ในการโหลดหน้า คุณจะเห็นด้านล่างว่าการกระทำ "application_controller#home" ทำการสืบค้น MySQL 2 ครั้ง

Rendered people/_person.html.erb (0.3ms)
Rendered people/_person.html.erb (0.2ms)
Rendered people/_person.html.erb (0.3ms)
Rendered people/_person.html.erb (0.3ms)
Rendered people/_person.html.erb (0.3ms)
Rendered people/_person.html.erb (0.3ms)
Rendered people/_person.html.erb (0.3ms)
Rendered people/_person.html.erb (0.2ms)

Rendered application/home.html.erb within layouts/application (936.0ms)
Completed 200 OK in 936ms (Views: 927.1ms | ActiveRecord: 9.3ms)

2. โหลดเฉพาะสิ่งที่คุณจะใช้

นี่คือหน้าตาของหน้าแรก

คุณจะเห็นได้ว่าเราต้องการแค่ที่อยู่เท่านั้น ไม่มีอะไรอื่น แต่ในส่วน "_person.html.erb" เราโหลดอ็อบเจ็กต์โปรไฟล์ มาดูกันว่าเราจะทำการเปลี่ยนแปลงนั้นได้อย่างไร

<li>
  Addresses:
  <ul>
    <% person.profiles.pluck(:address).each do |address| %>
      <li><%= address %></li>
    <% end %>
  </ul>
</li>

สำหรับข้อมูลการสืบค้น N+1 ในเชิงลึก โปรดอ่านประสิทธิภาพ ActiveRecord:รูปแบบการสืบค้น N+1

ProTip:คุณสามารถสร้างขอบเขตสำหรับสิ่งนี้และเพิ่มลงในไฟล์ "models/profile.rb" การสืบค้นฐานข้อมูลดิบในไฟล์มุมมองของคุณไม่ได้มีประโยชน์อะไรมาก

3. ย้ายการเรียกฐานข้อมูลทั้งหมดไปยังคอนโทรลเลอร์

สมมติว่าในอนาคตของแอปพลิเคชันปลอมนี้ คุณต้องการแสดงจำนวนผู้ใช้ทั้งหมดในหน้าแรก

เรียบง่าย! ลองโทรในมุมมองที่มีลักษณะดังนี้:

# of People: <%= @people.count %>

โอเค ง่ายพอ

มีข้อกำหนดอื่น คุณต้องสร้างองค์ประกอบ UI ที่แสดงความคืบหน้าของหน้า ทีนี้มาหารจำนวนคนบนเพจด้วยจำนวนทั้งหมดกัน

Progress: <%= index / @people.count %>

ขออภัย เพื่อนร่วมงานของคุณไม่ทราบว่าคุณได้ทำการสืบค้นข้อมูลนี้แล้ว และพวกเขากำลังดำเนินการค้นหาซ้ำแล้วซ้ำอีกในมุมมอง

หากคอนโทรลเลอร์ของคุณมีลักษณะดังนี้:

def home
  @people = Person.all.includes(:profiles)
  @people_count = @people.count
end

การนำตัวแปรที่คำนวณไปแล้วมาใช้ซ้ำจะง่ายกว่า

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

4. กำหนดเลขหน้าได้ทุกที่!

เช่นเดียวกับการโหลดเฉพาะสิ่งที่คุณต้องการ มันช่วยแสดงเฉพาะสิ่งที่คุณต้องการด้วย! ด้วยการแบ่งหน้า มุมมองจะแสดงข้อมูลบางส่วนและเก็บส่วนที่เหลือให้โหลดตามต้องการ วิธีนี้ช่วยประหยัดเวลาได้มากในไม่กี่วินาที! อัญมณี will_paginate และ kaminari ช่วยคุณได้ในเวลาไม่กี่นาที

ความรำคาญอย่างหนึ่งที่ทำให้เกิดสิ่งนี้คือผู้ใช้ต้องคลิกที่ "หน้าถัดไป" ต่อไป ในการนั้น คุณยังสามารถดูที่ "Infinite Scrolling" เพื่อให้ผู้ใช้ของคุณได้รับประสบการณ์ที่ดียิ่งขึ้น

หลีกเลี่ยงการโหลด HTML ซ้ำ

ในแอป Rails แบบดั้งเดิม การแสดงมุมมอง HTML ใช้เวลานาน โชคดีที่มีมาตรการเพื่อลดปัญหานี้

1. เทอร์โบลิงค์

สิ่งนี้รวมอยู่ในแอพ Rails มาตรฐานของคุณ Turbolinks คือไลบรารี JavaScript ที่ทำงานได้ทุกที่ (แม้จะไม่มี Rails ก็ตาม เช่น ในหน้าสแตติก) และลดระดับลงอย่างสวยงามในเบราว์เซอร์ที่ไม่รองรับ

มันแปลงทุกลิงก์เป็นคำขอ AJAX และแทนที่เนื้อหาทั้งหมดของหน้าผ่าน JS วิธีนี้ช่วยเพิ่มประสิทธิภาพได้อย่างมาก เนื่องจากไม่ต้องโหลด CSS, JS และรูปภาพซ้ำ

อย่างไรก็ตาม เมื่อเขียน JS แบบกำหนดเอง คุณจะต้องใช้ความระมัดระวังเป็นพิเศษในการเขียน "Turbolinks safe JS" อ่านเพิ่มเติมเกี่ยวกับเรื่องนี้ได้ที่นี่

2. ใช้คำขอ AJAX

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

มาดูการทำงานของ AJAX!

ในแอปตัวอย่าง จะมีปุ่ม "โหวต" สำหรับผู้ใช้แต่ละคน มาวัดกันว่าจะใช้เวลานานเท่าใดในการดำเนินการนั้น

Started POST "/people/1/vote" for 127.0.0.1 at 2020-01-21 14:50:49 +0530
Processing by PeopleController#vote as HTML
  Person Load (0.3ms)  SELECT  "people".* FROM "people" WHERE "people"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
   (0.1ms)  begin transaction
  Person Exists (4.5ms)  SELECT  1 AS one FROM "people" WHERE "people"."name" = ? AND ("people"."id" != ?) LIMIT ?  [["name", "Deon Waelchi"], ["id", 1], ["LIMIT", 1]]
  SQL (1.0ms)  UPDATE "people" SET "votes_count" = ?, "updated_at" = ? WHERE "people"."id" = ?  [["votes_count", 1], ["updated_at", "2020-01-21 09:20:49.941928"], ["id", 1]]

Redirected to https://localhost:3000/
Completed 302 Found in 24ms (ActiveRecord: 7.5ms)


Started GET "/" for 127.0.0.1 at 2020-01-21 14:50:49 +0530
Processing by ApplicationController#home as HTML
  Rendering application/home.html.erb within layouts/application

  Rendered people/_person.html.erb (2.4ms)
   (0.3ms)  SELECT "profiles"."address" FROM "profiles" WHERE "profiles"."person_id" = ?  [["person_id", 30]]
  Rendered people/_person.html.erb (2.2ms)
  ...

  Rendered application/home.html.erb within layouts/application (159.8ms)
Completed 200 OK in 190ms (Views: 179.0ms | ActiveRecord: 6.8ms)

ซึ่งใช้เวลาเท่ากันกับการโหลดหน้าใหม่ บวกกับส่วนเพิ่มเติมเล็กน้อยสำหรับส่วนการลงคะแนนจริง

มาทำให้เป็นคำขอ AJAX ตอนนี้ "people/_person.html.erb" ของเรามีลักษณะดังนี้:

<%= button_to "Vote #{person.votes_count}", vote_person_path(person), remote: true %>

การดำเนินการควบคุมของเราส่งคืนการตอบกลับ JS ซึ่งมีลักษณะดังนี้:

$("#<%= @person.id %>").html("<%= j render(partial: 'person', locals: {person: @person}) %>");

อย่างที่คุณเห็น เรากำลังแทนที่เฉพาะเนื้อหาที่เราต้องการ เราจัดเตรียมรหัส HTML เพื่อเชื่อมต่อกับ div และแทนที่ แน่นอน เราสามารถเพิ่มประสิทธิภาพสิ่งนี้ได้ด้วยการแทนที่เฉพาะเนื้อหาปุ่ม แต่สำหรับจุดประสงค์ของโพสต์นี้ เราจะมาแทนที่บางส่วนทั้งหมด

ผลลัพธ์?

Started POST "/people/1/vote" for 127.0.0.1 at 2020-01-21 14:52:56 +0530
Processing by PeopleController#vote as JS

  Person Load (0.2ms)  SELECT  "people".* FROM "people" WHERE "people"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
   (0.1ms)  begin transaction
  Person Exists (0.3ms)  SELECT  1 AS one FROM "people" WHERE "people"."name" = ? AND ("people"."id" != ?) LIMIT ?  [["name", "Deon Waelchi"], ["id", 1], ["LIMIT", 1]]
  SQL (0.4ms)  UPDATE "people" SET "votes_count" = ?, "updated_at" = ? WHERE "people"."id" = ?  [["votes_count", 2], ["updated_at", "2020-01-21 09:22:56.532281"], ["id", 1]]
   (1.6ms)  commit transaction
  Rendering people/vote.js.erb
   (0.2ms)  SELECT "profiles"."address" FROM "profiles" WHERE "profiles"."person_id" = ?  [["person_id", 1]]

  Rendered people/_person.html.erb (3.2ms)
  Rendered people/vote.js.erb (6.3ms)
Completed 200 OK in 31ms (Views: 14.6ms | ActiveRecord: 2.9ms)

30ms! แค่นั้นแหละ! มันยอดเยี่ยมแค่ไหน

เคล็ดลับจากมือโปร:หากคุณไม่ต้องการยุ่งกับรหัส HTML และคลาสจำนวนมากเพื่อค้นหาว่าเมื่อใด/อะไรที่จะแทนที่ ให้พิจารณาใช้ render_async gem มันช่วยยกของหนักออกจากกล่องได้มาก

3. ใช้เว็บซ็อคเก็ต

สิ่งที่ยอดเยี่ยมประการหนึ่งเกี่ยวกับการรีโหลด HTML คือทำให้คุณได้รับเนื้อหาที่สดใหม่จากเซิร์ฟเวอร์ทุกครั้ง ด้วยคำขอ AJAX คุณจะเห็นเฉพาะเนื้อหาล่าสุดสำหรับข้อมูลโค้ดเล็กๆ น้อยๆ

WebSockets เป็นเทคโนโลยีที่ยอดเยี่ยมที่ช่วยให้เซิร์ฟเวอร์ของคุณส่งการอัปเดตไปยังไคลเอนต์ แทนที่จะเป็นไคลเอนต์ที่ขอข้อมูลใหม่

ซึ่งจะมีประโยชน์เมื่อคุณต้องการสร้างหน้าเว็บแบบไดนามิก ลองนึกภาพว่าคุณต้องแสดงคะแนนของเกมบนเว็บไซต์ของคุณ คุณสามารถดึงเนื้อหาใหม่ได้

  • แจ้งให้ผู้ใช้ของคุณโหลดซ้ำทั้งหน้า
  • ระบุปุ่มโหลดซ้ำเพื่อรีเฟรชเฉพาะคะแนน
  • ใช้ JavaScript เพื่อทำการโพลแบ็กเอนด์ทุกวินาที
    • การดำเนินการนี้จะทำการ ping เซิร์ฟเวอร์ต่อไปแม้ว่าข้อมูลจะไม่มีการเปลี่ยนแปลง
    • ลูกค้าแต่ละรายจะโทรออกทุกวินาที - ทำให้เซิร์ฟเวอร์ล้นหลามอย่างง่ายดาย
  • ใช้ WebSockets!

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

Rails 5 ได้เปิดตัว ActionCable ซึ่งช่วยให้คุณจัดการ WebSockets ได้ทั้งหมด มีกรอบงาน JS สำหรับลูกค้าเพื่อสมัครรับข้อมูลจากเซิร์ฟเวอร์และกรอบงานส่วนหลังสำหรับเซิร์ฟเวอร์เพื่อเผยแพร่การเปลี่ยนแปลง ด้วยสายเคเบิลสำหรับการดำเนินการ คุณสามารถเลือกบริการ WebSocket ที่คุณต้องการได้ อาจเป็น Faye บริการเว็บซ็อกเก็ตที่จัดการด้วยตนเอง หรือบริการสมัครรับข้อมูล

โดยส่วนตัวแล้ว ฉันจะเลือกการสมัครรับข้อมูลนี้ เนื่องจากจะช่วยลดจำนวนสิ่งที่คุณต้องจัดการ

โอเค กลับไปที่ WebSockets เมื่อคุณตั้งค่า ActionCable เสร็จแล้ว มุมมองของคุณจะไม่สามารถฟังอินพุต JSON จากเซิร์ฟเวอร์ได้ เมื่อได้รับแล้ว การดำเนินการของ hook ที่คุณเขียนจะแทนที่เนื้อหา HTML ที่เกี่ยวข้อง

เอกสาร Rails และ Pusher มีบทช่วยสอนที่ยอดเยี่ยมเกี่ยวกับวิธีการสร้างด้วย WebSockets พวกเขาต้องอ่าน!

แคช

เวลาในการโหลดส่วนใหญ่ถูกใช้จนหมดในการแสดงผลมุมมอง ซึ่งรวมถึงการโหลด CSS, JS และรูปภาพทั้งหมด การแสดงผล HTML จากไฟล์ ERB และอื่นๆ

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

ในตัวอย่างของเรา เห็นได้ชัดว่าจนกว่าจะมีคนโหวต หน้าแรกจะมีลักษณะเหมือนกันสำหรับทุกคน (ขณะนี้ไม่มีตัวเลือกให้ผู้ใช้แก้ไขที่อยู่ของตน) ลองแคชทั้งหน้า "home.html.erb" จนกว่าจะมีกิจกรรม (โหวต) เกิดขึ้น

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

1. แคชมุมมอง

เวลาในการโหลดสำหรับ 2000 ระเบียนโดยไม่แคชคือ 3500 มิลลิวินาที!

แคชทุกอย่างใน "home.html.erb" กันเถอะ ง่ายๆ

<% cache do %>
  <ul>
    <% @people.each do |person| %>
      <li id="<%= person.id %>"><%= render person %></li>
    <% end %>
  </ul>
<% end %>

ถัดไป ติดตั้ง Dalli gem และเปลี่ยนที่จัดเก็บแคชใน "development.rb" เป็น:

config.cache_store = :dalli_store

จากนั้น หากคุณใช้ Mac หรือ Linux เพียงเริ่มบริการ Memcached ดังนี้:

memcached -vv

มาโหลดกันใหม่!!

ใช้เวลาประมาณ 537ms! นั่นคือความเร็วที่เพิ่มขึ้น 7 เท่า!

นอกจากนี้ คุณจะเห็นว่ามีการสืบค้น MySQL น้อยกว่ามาก เนื่องจาก HTML ทั้งหมดถูกเก็บไว้ใน Memcached และอ่านจากที่นั่นอีกครั้ง โดยไม่ต้องส่ง Ping ฐานข้อมูลของคุณ

หากคุณเปิดไปที่บันทึกของแอปพลิเคชัน คุณจะเห็นว่าหน้านี้อ่านจากแคชทั้งหมดด้วย

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

2. การสืบค้นฐานข้อมูลแคช

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

ตัวอย่างเช่น สมมติว่าแอปพลิเคชันจำเป็นต้องแสดงขนาดของทีมผู้ใช้ นี่อาจเป็นการคำนวณที่ซับซ้อนซึ่งเกี่ยวข้องกับการนับผู้รายงานโดยตรง ที่ปรึกษาจากภายนอก และอื่นๆ

แทนที่จะทำการคำนวณซ้ำแล้วซ้ำอีก คุณสามารถแคชได้!

def team_size
  Rails.cache.fetch(:team_size, expires_in: 8.hour) do
    analytics_client = AnalyticsClient.query!(self)
    analytics_client.team_size
  end
end

แคชนี้จะหมดอายุโดยอัตโนมัติหลังจาก 8 ชั่วโมง เมื่อสิ่งนั้นเกิดขึ้น การคำนวณจะดำเนินการอีกครั้งและค่าล่าสุดจะถูกแคชไว้เป็นเวลา 8 ชั่วโมงข้างหน้า

3. ดัชนีฐานข้อมูล

คุณยังสามารถเร่งความเร็วการสืบค้นโดยใช้ดัชนี แบบสอบถามง่ายๆ เพื่อดึงที่อยู่ทั้งหมดของบุคคล

person.addresses

แบบสอบถามนี้ขอให้ตารางที่อยู่ส่งคืนที่อยู่ทั้งหมดที่ person_id คอลัมน์คือ person.id . หากไม่มีดัชนี ฐานข้อมูลจะต้องตรวจสอบแต่ละแถวแยกกันเพื่อตรวจสอบว่าตรงกับ person.id . อย่างไรก็ตาม ด้วยดัชนี ฐานข้อมูลมีรายการของที่อยู่ที่ตรงกับ person.id .

นี่คือแหล่งข้อมูลที่ยอดเยี่ยมในการเรียนรู้เพิ่มเติมเกี่ยวกับดัชนีฐานข้อมูล!

สรุป

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

หากคุณต้องการปรับปรุงประสิทธิภาพของแอป ให้เริ่มต้นง่ายๆ และวัดผลต่อไป ล้างการสืบค้นฐานข้อมูลของคุณ จากนั้นสร้างคำขอ AJAX ทุกที่ที่ทำได้ และสุดท้ายแคชมุมมองให้มากที่สุด คุณสามารถไปยัง WebSockets และการแคชฐานข้อมูลได้หลังจากนั้น

อย่างไรก็ตาม โปรดใช้ความระมัดระวัง—การเพิ่มประสิทธิภาพเป็นทางลาดลื่น คุณอาจพบว่าตัวเองติดยาเสพติดเหมือนฉัน!

ป.ล. สำหรับการตรวจสอบประสิทธิภาพของแอป Rails ในการผลิต ให้ตรวจสอบ APM ของ AppSignal ซึ่งสร้างโดย Ruby devs สำหรับ Ruby devs 🚀