12/08/2018, 15:23

Cải thiện tốc độ cho Rails App (Phần 1)

Lúc mới bắt đầu 1 project cá nhân, outsource thì thường mọi người chỉ hứng thú với một vài tháng đầu, và sau đấy thì khi mà project đã bắt đầu "phình to" lên, khi mà đã có 1 số lượng người dùng nhất định, các web requests bắt đầu chậm dần, và số tài nguyên mà database sử dụng thì tăng lên 1 cách ...

Lúc mới bắt đầu 1 project cá nhân, outsource thì thường mọi người chỉ hứng thú với một vài tháng đầu, và sau đấy thì khi mà project đã bắt đầu "phình to" lên, khi mà đã có 1 số lượng người dùng nhất định, các web requests bắt đầu chậm dần, và số tài nguyên mà database sử dụng thì tăng lên 1 cách kinh khủng. Có bao giờ bạn tự hỏi, mình có thay đổi cái gì đâu mà sao app của mình ngày càng chậm dần đều vậy?

Và có cái cách nào để cải thiện cái vấn đề chậm dần đều này không, hay là vốn bản thân Rails App đã chậm, cồng kềnh rồi nên không thể nào mà scaleable được?

faster-rails-is-your-database-properly-indexed-6b713688

Hmm, có hàng tá lí do đằng sau làm ứng dụng của bạn chậm đi, và các câu truy vấn tới database thường là thủ phạm đầu tiên ảnh hưởng đến performance. Loading quá nhiều data vào bộ nhớ, các câu truy vấn N+1, không cache các data trả về, và sử dụng (hay không có sử dụng) 1 cách hợp lí database indexing là 1 trong những nguyên nhân hàng đầu làm Rails App của bạn chậm dần đều đi :v.

Khi mà bạn tạo 1 column, thì rõ ràng là bạn muốn lưu và nhận dữ liệu từ cái column đấy rồi :DD.

Giả sử là chúng ta có 1 model Project, và tất cả các project này đều có thuộc tính là name chẳng hạn. Khi mà người dùng muốn truy cập vào trang show detail của project này: http://your-slow-rails-app/projects/project-name, thì điều đầu tiên mà chúng ta phải xử lí trong controller là phải tìm project dựa theo tên project đấy

  project = Project.find_by_name(params[:name])

Và với việc không có index, thì database của bạn sẽ phải kiểm tra tất cả các bản ghi trong bảng projects, cho tới khi tìm được thì thôi.

Tuy nhiên, nếu mà chúng ta khai báo index như ví dụ dưới đây thì câu lệnh trên sẽ trả về kết quả 1 cách rõ rệt

class IndexProjectsOnName < ActiveRecord::Migration
  def change
    add_index :projects, :name
  end
end

Để mường tượng rõ hơn 1 chút thì chúng ta nên tưởng tưởng index như là 1 cái mục lục của 1 cuốn từ điển vậy. Nếu như chúng ta muốn tìm 1 từ trong đó thì chúng ta có 2 cách là phải đọc từ đầu đến cuối đến từ chúng ta cần tìm thì dừng, hoặc chúng ta chỉ việc mở trang mục lục ra và tìm tới vị trí của từ đó =)).

Mình đã nói qua cách tạo index, tại sao phải tạo index rồi. Vậy giờ thì nên index những trường nào của bảng thì hợp lí? Thì có 1 lời khuyên bạn nên index tất cả những cái gì mà được dùng trong WHERE, ORDER_BY, HAVING trong câu truy vấn SQL của mình.

Về ví dụ:

User.find_by_username("shiroyasha")
User.find_by_email("support@semaphoreci.com")

thì chúng ta nên tạo index cho trường username, hoặc email:

add_index :users, :username
add_index :users, :email

Index cho các khoá ngoại

Nếu như mà bạn có nhiều mối quan hệ belongs_to, has_many, bạn nên index những khoá ngoại này để tăng tốc độ tìm kiếm.

Về ví dụ, ta có 1 mối quan hệ như này:

class Project < ActiveRecord::Base
  has_many :branches
end

class Branch < ActiveRecord::Base
  belongs_to :project
end

Để tăng tốc độ truy vấn cho SQL, chúng ta cần thêm index như sau:

add_index :branches, :project_id

Với 1 mối quan hệ đa hình:

class Organization < ActiveRecord::Base
  has_many :projects, :as => :owner
end

class User < ActiveRecord::Base
  has_many :projects, :as => :owner
end

class Project < ActiveRecord::Base
  belongs_to :owner, :polymorphic => true
end

Thì chúng ta phải tạo index với 2 trường ✌️

# Tạo như này thì sẽ không tăng tốc độ xử lí :v
add_index :projects, :owner_id
add_index :projects, :owner_type

# Nên tạo với trường id, type
add_index :projects, [:owner_id, :owner_type]

Index cho các giá trị ordered

Chúng ta nên index cho những trường nào hay được sử dụng để sorting như updated_at, created_at.

Ví dụ:

Build.order(:updated_at).take(10)

với câu lệnh này thì chúng ta có thể tăng tốc độ xử lí với cách đặt index như sau:

add_index :updated_at

Với việc sử dụng index cho các trường quan trọng thì chúng ta có thể tăng tốc độ rõ rệt cho ứng dụng, nhưng trong 1 số trường hợp thì có thể làm ứng dụng của bạn chậm đi nhiều.

Ví dụ, bảng mà thường có nhiều phần tử bị xoá mà chúng ta sử dụng index cho những trường này có thể gây ảnh hưởng xấu tới hiệu suất. Hoặc là nếu chúng ta tạo index cho 1 trường mà có hàng triệu records thì cũng gây lãng phí tài nguyên lưu trữ.

Cho nên mỗi lần chúng ta tạo index thì nên suy nghĩ rõ ràng, dựa theo các data, đánh giá từ môi trường thực tế để đánh index đúng với nhu cầu.

Nguồn: https://semaphoreci.com/blog/2017/05/09/faster-rails-is-your-database-properly-indexed.html?utmsource=rubyweekly&utmmedium=email

0