1. Khái Niệm và Ứng Dụng Của Chỉ Số

1.1 Chỉ Số Là Gì

Một chỉ số là một cấu trúc dữ liệu được sử dụng trong hệ thống quản lý cơ sở dữ liệu để tăng tốc độ các hoạt động truy xuất dữ liệu. Nó có thể được xem như một "bảng chỉ dẫn" trong cơ sở dữ liệu, cho phép nhanh chóng xác định vị trí của dữ liệu trong bảng dữ liệu, tránh quét toàn bộ bảng và cải thiện đáng kể hiệu suất truy vấn. Trong ứng dụng thực tế, việc sử dụng chỉ số đúng cách có thể cải thiện đáng kể hiệu suất của cơ sở dữ liệu.

1.2 Các Loại Chỉ Số

Có nhiều loại chỉ số, mỗi loại có thiết kế và tối ưu hóa riêng cho các kịch bản ứng dụng khác nhau:

  • Chỉ Số Trường Đơn: Một chỉ số chỉ bao gồm một trường, phù hợp cho các kịch bản dựa trên một điều kiện duy nhất để truy vấn nhanh chóng.
  • Chỉ Số Composite: Một chỉ số bao gồm nhiều trường, cung cấp tối ưu hóa cho các truy vấn chứa các trường này.
  • Chỉ Số Duy Nhất: Đảm bảo tính duy nhất của các trường chỉ số, ngăn chặn sự tồn tại của các giá trị trùng lặp. Chỉ số duy nhất có thể là chỉ số trường đơn hoặc composite.

2. Định Nghĩa Chỉ Số

2.1 Định Nghĩa Chỉ Số Trường

Tạo một chỉ số trường đơn liên quan đến việc thiết lập chỉ số trên một cột cụ thể của bảng dữ liệu, có thể thực hiện bằng cách sử dụng phương thức Fields. Ví dụ dưới đây minh họa cách tạo chỉ số trên trường phone của thực thể User.

func (User) Fields() []ent.Field {
    return []ent.Field{
        field.String("phone"),
    }
}
func (User) Indexes() []ent.Index {
    // Tạo một chỉ số trường đơn.
    return []ent.Index{
        index.Fields("phone"),
    }
}

Trong đoạn mã này, trường phone được tạo chỉ mục. Điều này cho phép hệ thống sử dụng chỉ mục để tìm kiếm nhanh chóng khi truy vấn trường phone.

2.2 Định Nghĩa Chỉ Số Duy Nhất

Chỉ mục duy nhất đảm bảo tính duy nhất của dữ liệu trên các cột chỉ mục. Nó có thể được tạo bằng cách thêm phương thức Unique vào trường. Các ví dụ sau minh họa việc tạo chỉ mục duy nhất cho trường đơn và nhiều trường.

Tạo chỉ mục duy nhất cho một trường đơn:

func (User) Fields() []ent.Field {
    return []ent.Field{
        field.String("email").Unique(),
    }
}

Trong ví dụ này, trường email được chỉ định là duy nhất, đảm bảo rằng mỗi địa chỉ email của người dùng là duy nhất trong cơ sở dữ liệu.

Tạo chỉ mục duy nhất cho kết hợp của nhiều trường:

func (User) Indexes() []ent.Index {
    return []ent.Index{
        // Tạo chỉ mục duy nhất cho nhiều trường.
        index.Fields("first_name", "last_name").Unique(),
    }
}

Đoạn mã này xác định một chỉ mục duy nhất kết hợp cho kết hợp của các trường first_namelast_name, ngăn chặn việc xuất hiện các bản ghi có giá trị giống nhau trong cả hai trường.

2.3 Định Nghĩa Chỉ Số Composite

Khi các điều kiện truy vấn liên quan đến nhiều trường, một chỉ mục composite có thể được sử dụng. Chỉ mục composite lưu trữ dữ liệu theo thứ tự được xác định trong chỉ mục. Thứ tự của chỉ mục có tác động đáng kể đến hiệu suất truy vấn, vì vậy khi định nghĩa một chỉ mục composite, thứ tự của các trường cần phải được xác định dựa trên mẫu truy vấn.

Dưới đây là một ví dụ về việc tạo chỉ mục composite:

func (User) Indexes() []ent.Index {
    return []ent.Index{
        // Tạo một chỉ mục composite với nhiều trường.
        index.Fields("country", "city"),
    }
}

Trong ví dụ này, chỉ mục composite được tạo trên các trường countrycity. Điều này có nghĩa là khi thực hiện các hoạt động truy vấn liên quan đến hai trường này, cơ sở dữ liệu có thể nhanh chóng xác định dữ liệu thỏa mãn các điều kiện.

Một chỉ mục composite không chỉ tăng tốc độ hiệu suất truy vấn mà còn hỗ trợ các hoạt động sắp xếp dựa trên chỉ mục, cung cấp hiệu suất truy xuất dữ liệu hiệu quả hơn. Khi thiết kế một chỉ mục composite, thường đặt các trường có tính lựa chọn cao ở phía trước của chỉ mục để trình tối hóa cơ sở dữ liệu có thể tốt hơn sử dụng chỉ mục.

3. Các chỉ số cạnh

Trong framework ent, các chỉ số cạnh là cách để xác định các chỉ số thông qua các cạnh (mối quan hệ). Cơ chế này thường được sử dụng để đảm bảo tính duy nhất của các trường dưới các mối quan hệ cụ thể. Ví dụ, nếu mô hình cơ sở dữ liệu của bạn bao gồm các thành phố và đường phố, và mỗi tên đường phố dưới một thành phố cần phải duy nhất, chỉ số cạnh có thể được sử dụng để đạt được điều này.

// Tệp ent/schema/street.go xác định cấu trúc của đối tượng Street.
type Street struct {
    ent.Schema
}

func (Street) Fields() []ent.Field {
    // Xác định các trường
    return []ent.Field{
        field.String("name"),
    }
}

func (Street) Edges() []ent.Edge {
    // Xác định mối quan hệ cạnh với City, nơi Street thuộc về một City.
    return []ent.Edge{
        edge.From("city", City.Type).
            Ref("streets").
            Unique(),
    }
}

func (Street) Indexes() []ent.Index {
    // Tạo một chỉ số duy nhất thông qua cạnh để đảm bảo tính duy nhất của tên đường phố trong cùng một thành phố.
    return []ent.Index{
        index.Fields("name").
            Edges("city").
            Unique(),
    }
}

Trong ví dụ này, chúng ta tạo một thực thể Street và liên kết nó với thực thể City. Bằng cách xác định một chỉ số cạnh trong phương thức Indexes của thực thể Street, chúng ta đảm bảo rằng tên của mỗi con đường dưới một thành phố là duy nhất.

Chương 5: Tùy chọn chỉ số nâng cao

5.1 Chỉ số Toàn văn và Chỉ số Băm

Chỉ số toàn văn và chỉ số băm là hai loại chỉ số duy nhất trong MySQL và PostgreSQL, và chúng được sử dụng cho các tình huống tối ưu truy vấn khác nhau.

Chỉ số toàn văn thường được sử dụng để tìm kiếm dữ liệu văn bản, đặc biệt khi bạn cần thực hiện các tìm kiếm phức tạp, như tìm kiếm phù hợp từ khóa. Cả cơ sở dữ liệu MySQL và PostgreSQL đều hỗ trợ chỉ số toàn văn. Ví dụ, trong MySQL, bạn có thể xác định một chỉ số toàn văn như sau:

// Tệp ent/schema/user.go xác định cấu trúc của đối tượng User
func (User) Indexes() []ent.Index {
    // Tạo một chỉ số toàn văn sử dụng danh mục FULLTEXT trong MySQL
    return []ent.Index{
        index.Fields("description").
            Annotations(entsql.IndexTypes(map[string]string{
                dialect.MySQL: "FULLTEXT",
            })),
    }
}

Chỉ số băm đặc biệt phù hợp cho các truy vấn tương đương và không hỗ trợ sắp xếp và truy vấn phạm vi. Trong PostgreSQL, bạn có thể sử dụng một chỉ số băm như sau:

func (User) Indexes() []ent.Index {
    // Xác định một chỉ số loại HASH
    return []ent.Index{
        index.Fields("c4").
            Annotations(entsql.IndexType("HASH")),
    }
}

5.2 Chỉ số Một phần và Tiền tố chỉ số

Chỉ số một phần là một loại chỉ số chỉ lập chỉ mục các hàng trong một bảng thỏa mãn điều kiện cụ thể. Trong SQLite và PostgreSQL, bạn có thể sử dụng mệnh đề WHERE để tạo chỉ số một phần.

Ví dụ, xác định một chỉ số một phần trong PostgreSQL:

func (User) Indexes() []ent.Index {
    // Tạo một chỉ số một phần trên trường "nickname", chỉ chứa các hàng khi "active" là true
    return []ent.Index{
        index.Fields("nickname").
            Annotations(
                entsql.IndexWhere("active"),
            ),
    }
}

Chỉ số tiền tố đặc biệt hữu ích cho các trường văn bản, đặc biệt là trong MySQL. Nó có thể rút ngắn thời gian tạo chỉ mục, giảm không gian chiếm bởi chỉ số, và cũng cung cấp hiệu suất tốt. Như được hiển thị dưới đây, cho MySQL, bạn có thể xác định một chỉ số với tiền tố:

func (User) Indexes() []ent.Index {
    // Tạo một chỉ số sử dụng tiền tố chỉ số
    return []ent.Index{
        index.Fields("description").
            Annotations(entsql.Prefix(128)),
    }
}

5.3 Chú thích Chỉ số và Tùy chỉnh

Trong ent, Annotations là một tính năng cho phép các nhà phát triển tùy chỉnh các chỉ số. Bạn có thể xác định loại chỉ số, thiết lập quy tắc sắp xếp, và nhiều hơn nữa.

Ví dụ, đoạn mã sau minh họa cách xác định quy tắc sắp xếp cột trong một chỉ số:

func (User) Indexes() []ent.Index {
    return []ent.Index{
        // Sử dụng chú thích để xác định quy tắc sắp xếp cột của chỉ số
        index.Fields("c1", "c2", "c3").
            Annotations(entsql.DescColumns("c1", "c2")),
    }
}

Với tính năng chú thích, các nhà phát triển có thể linh hoạt tùy chỉnh chỉ số để tối ưu hiệu suất và cấu trúc của cơ sở dữ liệu.