12/08/2018, 15:23

Khi nào cần đánh index trong cơ sở dữ liệu

Các dự án Rails ban đầu thường sẽ hoạt động nhanh và ổn định. Nhưng sau khoảng vài tháng, khi số lượng người dùng sản phẩm bắt đầu tăng lên, các web request sẽ trở nên chậm hơn, việc sử dụng CPU của cơ sở dữ liệu tăng lên quá cao. Điều này dẫn đến việc, mặc dù chúng ta không thay đổi gì nhưng sản ...

Các dự án Rails ban đầu thường sẽ hoạt động nhanh và ổn định. Nhưng sau khoảng vài tháng, khi số lượng người dùng sản phẩm bắt đầu tăng lên, các web request sẽ trở nên chậm hơn, việc sử dụng CPU của cơ sở dữ liệu tăng lên quá cao. Điều này dẫn đến việc, mặc dù chúng ta không thay đổi gì nhưng sản phẩm sẽ càng ngày càng trở nên chậm hơn. Vậy câu hỏi đặt ra là có cách nào để giải quyết vấn đề này, hay đơn giản bởi vì Rails không có khả năng mở rộng?

Điều gì làm cho ứng dụng Rails của bạn chậm?

Có thể có nhiều lí do khác nhau đằng sau sự chậm trễ của ứng dụng, tuy nhiên các truy vấn cơ sở dữ liệu thường đóng vai trò lớn nhất trong hiệu suất của một ứng dụng Rails. Load quá nhiều dữ liệu vào bộ nhớ, N+1 truy vấn, thiếu cache, thiếu index cho cơ sở dữ liệu là những nguyên nhân lớn nhất gây ra request chậm. Việc thiếu các index cho khóa ngoài, các cột hay được tìm kiếm, hoặc các giá trị cần được sắp xếp có thể tạo ra sự khác biệt rất lớn. Việc đánh index sẽ không đáng chú ý với những bảng chỉ có vài nghìn bản ghi, tuy nhiên khi dữ liệu của bảng đó nở ra vài triệu bản ghi, các tra cứu trong bảng sẽ trở nên rất chậm chạp.

Vai trò của index trong cơ sở dữ liệu

Khi bạn tạo một cột cơ sở dữ liệu, điều quan trọng là phải cân nhắc bạn có cần tìm và lấy các bản ghi dựa trên cột đó. Ví dụ, trong dự án của chúng ta có sử dụng một model tên là Project, có một attribute là name, mỗi khi có một request từ client muốn show thông tin của một project thì trong controller sẽ xử lý:

project = Project.find_by name: params[:name]

Nếu không có index cho cột name của bảng projects thì khi đoạn code trên được chạy, cơ sở dữ liệu sẽ cần check các bản ghi của bảng projects, từng cái một, cho đến khi tìm thấy hoặc duyệt qua hết tất cả các bản ghi. Tuy nhiên, nếu chúng ta thêm index cho cột name của bảng projects, tra cứu sẽ nhanh hơn nhiều.

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

Một cách rất hay để hiểu rõ hơn cơ chế hoạt động của index đó là chúng ta hãy tưởng tượng nó như mục lục trong mỗi cuốn sách. Bạn muốn tìm một phần nào đó, thay vì lật từng trang thì chúng ta sẽ tìm kiếm trong mục lục và đi thẳng đến trang có phần đó.

Những gì cần được đánh index

Một nguyên tắc chung là tạo index cho tất cả mọi thứ được tham chiếu trong các phần WHERE, HAVING và ORDER BY của các truy vấn SQL.

  • Index cho việc tìm kiếm giá trị duy nhất Bất kỳ tìm kiếm dựa trên một giá trị cột duy nhất thì nên có index. Ví dụ:
    User.find_by username: "shiroyasha"
    User.find_by email: "support@semaphoreci.com"
    
    Chúng ta sẽ thêm index vào cột username, email của bảng users
    add_index :users :username
    add_index :users, :email
    
  • Index cho khóa ngoài Nếu bạn có các mối quan hệ của belong_to hoặc has_many, bạn sẽ cần lập chỉ mục các khoá ngoại để tối ưu hóa việc tìm kiếm. Ví dụ chúng ta có các branches thuộc project
    class Project < ActiveRecord::Base
      has_many :branches
    end
    
    class Branch < ActiveRecord::Base
      belongs_to :project
    end
    
    Để tìm kiếm nhanh chúng ta cần thêm index như sau:
    add_index :branches, :project_id
    
    Trong trướng hợp có quan hệ sử dụng polymorphic, ví dụ owner của project có thể là user hoặc organization
    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 thêm index kép như sau:
    add_index :projects, [:owner_id, :owner_type]
    
    # add_index :projects, :owner_id
    # add_index :projects, :owner_type
    # Cách này sẽ không cải thiện tốc độ tìm kiếm
    
  • Index cho giá trị được sắp xếp Bất kỳ việc săp xếp nào xảy ra thường xuyên cũng có thể được cải tiến bằng cách sử dụng index dành riêng. Ví dụ:
    Build.order(:updated_at).take 10
    
    Có thể được cải thiện bằng cách thêm index dành riêng:
    add_index :updated_at
    

Có nên luôn luôn sử dụng index

Trong khi sử dụng các index cho các lĩnh vực quan trọng có thể cải thiện đáng kể hiệu suất ứng dụng của bạn, nhưng đôi lúc hiệu quả có thể là không đáng kể, hoặc nó thậm chí có thể làm cho ứng dụng của bạn chậm hơn. Ví dụ, các bảng có các phần tử thường xuyên bị xóa có thể tác động tiêu cực đến hiệu suất của cơ sở dữ liệu. Các bảng lớn với hàng triệu hồ sơ cũng đòi hỏi nhiều bộ nhớ hơn cho các index. Vì vậy, hãy luôn luôn hiểu về những thay đổi trong cơ sở dữ liệu của bạn, nếu không chắc chắn, hãy quyết định dựa trên số liệu đo thực tế.

Referrence: https://semaphoreci.com/blog/2017/05/09/faster-rails-is-your-database-properly-indexed.html

0