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