1. Pojęcie indeksu i jego zastosowanie
1.1 Czym jest indeks
Indeks jest strukturą danych używaną w systemie zarządzania bazą danych do przyspieszenia operacji pobierania danych. Może być postrzegany jako "katalog" w bazie danych, umożliwiający szybkie odnajdywanie danych w tabeli danych, unikając skanowania całej tabeli i znacząco poprawiając wydajność zapytań. W praktyce poprawne wykorzystanie indeksów może znacząco zwiększyć wydajność bazy danych.
1.2 Rodzaje indeksów
Istnieje wiele rodzajów indeksów, z każdym posiadającym swoje własne projekty i optymalizacje dla różnych scenariuszy aplikacji:
- Indeks jednopolowy: Indeks zawierający tylko jedno pole, odpowiedni dla scenariuszy zależnych od pojedynczego warunku szybkich zapytań.
- Indeks złożony: Indeks zawierający wiele pól, zapewniający optymalizację dla zapytań zawierających te pola.
- Indeks unikalny: Zapewnia unikalność pól indeksu, zabraniając istnienia zduplikowanych wartości. Indeksy unikalne mogą być jednopolowe lub złożone.
2. Definicja indeksu
2.1 Definicja indeksu pola
Tworzenie jednopolowego indeksu polega na ustanowieniu indeksu na określonej kolumnie tabeli danych, co można osiągnąć za pomocą metody Fields
. Poniższy przykład demonstruje, jak utworzyć indeks dla pola phone
encji User
.
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("phone"),
}
}
func (User) Indexes() []ent.Index {
// Utwórz jednopolowy indeks.
return []ent.Index{
index.Fields("phone"),
}
}
W tym fragmencie kodu pole phone
jest indeksowane. Pozwala to systemowi na wykorzystanie indeksu do szybszych wyszukiwań podczas zapytywania pola phone
.
2.2 Definicja unikalnego indeksu
Unikalny indeks zapewnia unikalność danych w indeksowanych kolumnach. Może być utworzony poprzez dodanie metody Unique
do pola. Poniższe przykłady ilustrują tworzenie unikalnych indeksów dla jednego i wielu pól.
Utworzenie unikalnego indeksu dla jednego pola:
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("email").Unique(),
}
}
W tym przykładzie pole email
jest określone jako unikalne, zapewniając, że każdy adres e-mail użytkownika jest unikalny w bazie danych.
Utworzenie unikalnego indeksu dla kombinacji wielu pól:
func (User) Indexes() []ent.Index {
return []ent.Index{
// Utwórz unikalny indeks dla wielu pól.
index.Fields("first_name", "last_name").Unique(),
}
}
Ten kod definiuje złożony unikalny indeks dla kombinacji pól first_name
i last_name
, zapobiegając występowaniu rekordów z takimi samymi wartościami w obu polach.
2.3 Definicja indeksu złożonego
Gdy warunki zapytania obejmują wiele pól, może być przydatny indeks złożony. Indeks złożony przechowuje dane w kolejności zdefiniowanej w indeksie. Kolejność indeksu ma znaczący wpływ na wydajność zapytań, dlatego przy definiowaniu indeksu złożonego konieczne jest określenie kolejności pól na podstawie wzorca zapytania.
Oto przykład tworzenia indeksu złożonego:
func (User) Indexes() []ent.Index {
return []ent.Index{
// Utwórz indeks złożony z wielu pól.
index.Fields("country", "city"),
}
}
W tym przykładzie tworzony jest indeks złożony dla pól country
i city
. Oznacza to, że podczas wykonywania operacji zapytania obejmujących te dwa pola, baza danych szybko może zlokalizować dane spełniające warunki.
Indeks złożony nie tylko przyspiesza wydajność zapytań, ale także obsługuje operacje sortowania oparte na indeksie, zapewniając bardziej efektywną wydajność pobierania danych. Przy projektowaniu indeksu złożonego często umieszcza się pola o wyższej selektywności z przodu indeksu, aby optymalizator bazy danych mógł lepiej wykorzystać indeks.
3. Indeksy krawędzi
W frameworku ent, indeksy krawędzi są sposobem definiowania indeksów poprzez krawędzie (relacje). Mechanizm ten jest typowo wykorzystywany do zapewnienia unikalności pól w określonych relacjach. Na przykład, jeśli model bazy danych zawiera miasta i ulice, a każda nazwa ulicy pod miastem musi być unikalna, indeksy krawędzi mogą być wykorzystane do osiągnięcia tego celu.
// W pliku ent/schema/street.go zdefiniowano schemat jednostki Street.
type Street struct {
ent.Schema
}
func (Street) Fields() []ent.Field {
// Zdefiniuj pola
return []ent.Field{
field.String("name"),
}
}
func (Street) Edges() []ent.Edge {
// Zdefiniuj relację krawędziową z City, gdzie Street należy do City.
return []ent.Edge{
edge.From("city", City.Type).
Ref("streets").
Unique(),
}
}
func (Street) Indexes() []ent.Index {
// Utwórz unikalny indeks poprzez krawędź w celu zapewnienia unikalności nazw ulic w ramach tego samego miasta.
return []ent.Index{
index.Fields("name").
Edges("city").
Unique(),
}
}
W tym przykładzie tworzymy jednostkę Street
i powiązujemy ją z jednostką City
. Poprzez zdefiniowanie indeksu krawędziowego w metodzie Indexes
jednostki Street
, zapewniamy, że nazwa każdej ulicy w ramach miasta jest unikalna.
Rozdział 5: Zaawansowane opcje indeksów
5.1 Indeksy pełnotekstowe i hashowe
Indeks pełnotekstowy i indeks hashowy to dwa unikalne typy indeksów w MySQL i PostgreSQL, i są wykorzystywane w różnych scenariuszach optymalizacji zapytań.
Indeks pełnotekstowy jest powszechnie używany do wyszukiwania danych tekstowych, szczególnie gdy konieczne jest wykonywanie bardziej skomplikowanych wyszukiwań, takich jak dopasowywanie słów. Zarówno bazy danych MySQL, jak i PostgreSQL obsługują indeksowanie pełnotekstowe. Na przykład, w MySQL, można zdefiniować indeks pełnotekstowy w ten sposób:
// W pliku ent/schema/user.go zdefiniowano schemat jednostki User.
func (User) Indexes() []ent.Index {
// Utwórz indeks pełnotekstowy przy użyciu kategorii FULLTEXT w MySQL
return []ent.Index{
index.Fields("description").
Annotations(entsql.IndexTypes(map[string]string{
dialect.MySQL: "FULLTEXT",
})),
}
}
Indeks hashowy jest szczególnie przydatny w przypadku zapytań równościowych i nie obsługuje sortowania ani zapytań zakresowych. W PostgreSQL można użyć indeksu haszowego w ten sposób:
func (User) Indexes() []ent.Index {
// Zdefiniuj indeks typu HASH
return []ent.Index{
index.Fields("c4").
Annotations(entsql.IndexType("HASH")),
}
}
5.2 Indeksy częściowe i prefiksy indeksów
Indeks częściowy to rodzaj indeksu, który indeksuje tylko wiersze w tabeli spełniające określone warunki. W SQLite i PostgreSQL można użyć klauzuli WHERE do tworzenia indeksów częściowych.
Na przykład, zdefiniowanie indeksu częściowego w PostgreSQL:
func (User) Indexes() []ent.Index {
// Utwórz indeks częściowy dla pola "nickname", zawierający tylko wiersze, w których "active" jest true
return []ent.Index{
index.Fields("nickname").
Annotations(
entsql.IndexWhere("active"),
),
}
}
Prefiks indeksu jest szczególnie przydatny dla pól tekstowych, zwłaszcza w MySQL. Może skrócić czas tworzenia indeksu, zmniejszyć zajmowane miejsce przez indeks, a także zapewnić dobrą wydajność. Jak pokazano poniżej, dla MySQL można zdefiniować indeks z prefiksem:
func (User) Indexes() []ent.Index {
// Utwórz indeks przy użyciu prefiksu indeksu
return []ent.Index{
index.Fields("description").
Annotations(entsql.Prefix(128)),
}
}
5.3 Adnotacje indeksów i dostosowywanie
W ent, Adnotacje
to funkcja, która umożliwia programistom dostosowywanie indeksów. Można zdefiniować rodzaj indeksu, ustawić reguły sortowania i nie tylko.
Na przykład, poniższy kod demonstruje, jak zdefiniować reguły sortowania kolumn w indeksie:
func (User) Indexes() []ent.Index {
return []ent.Index{
// Użyj adnotacji, aby zdefiniować reguły sortowania kolumn w indeksie
index.Fields("c1", "c2", "c3").
Annotations(entsql.DescColumns("c1", "c2")),
}
}
Dzięki funkcji adnotacji programiści mogą elastycznie dostosowywać indeksy, aby zoptymalizować wydajność i strukturę bazy danych.