Klucz do wydajności, czyli Indeksy w MongoDB

Cover Image for Klucz do wydajności, czyli Indeksy w MongoDB
Paweł
Paweł

Dziś porozmawiamy o indeksach w MongoDB. Omówimy:

  • czym są
  • wpływ na czas wykonania zapytań
  • tworzenie
  • rodzaje
  • jak dbać o ich wydajność

Artykuł będzie wymagał podstawowej wiedzy o MongoDB. Jeśli nie jesteś zaznajomiony z tym tematem lub nie czujesz się pewnie, możesz spróbować go przeczytać, ale niektóre terminy mogą być trudne do zrozumienia. Jest to zaledwie wstęp do tematu indeksów, rozwinięcia doczekasz się w kolejnych artykułach.

Indeksy

Czym w ogóle jest indeks?

Posłużmy się przykładem z życia codziennego. Na początku praktycznie każdej książki znajduje się spis treści. Dzięki niemu nie musisz wertować całości w poszukiwaniu interesujących fragmentów. Wystarczy przejrzeć posegregowaną listę rozdziałów i przejść na właściwą stronę.

Podobnie działa indeks bazodanowy. Zamiast przeszukiwać całą kolekcję danych, możemy przejrzeć posegregowana listę odsyłaczy do odpowiednich treści. Pozwala to na szybki dostęp do poszukiwanych informacji.

Jeżeli nasze zapytanie nie będzie oparte na żadnym indeksie to Mongo sprawdzi całą kolekcje próbując znaleźć wymagane elementy. Taka operacja nazywana jest collection scan. Opierając się na naszym przykładzie, musimy przejrzeć całą książkę w poszukiwaniu interesujących nas treści. Pomyśl co, jeżeli książka ma 1000 stron. Jeżeli nie jesteś po kursie szybkiego czytania to jej przeczytanie może chwile zająć.

Typy indeksów w MongoDB

MongoDB oferuje różne typy indeksów, które można dostosować do specyficznych potrzeb aplikacji:

1. Single Field Index: Najprostszy typ, który jest tworzony na jednym polu dokumentu. Jest idealny do prostych zapytań, opartych na jednym kryterium.

2. Compound Index: Indeks tworzony na wielu polach. Sprawdzi się w sytuacjach, gdy zapytanie wyszukiwania odwołuje się do kilku pól lub gdy używamy kilku kierunków sortowania.

3. Unique Index: Gwarantuje, że wartość pola indeksowanego jest unikalna w całej kolekcji. Jest często używany do wymuszania unikalności, np. w polach takich jak adresy e-mail. Najprostszym przykładem takiego indeksu jest ten, który z defaultu jest nakładany na pole “_id” w każdej kolekcji.

4. Text Index: Umożliwia pełnotekstowe wyszukiwanie w polach typu string. Jest używany do wyszukiwania fraz w dużych zbiorach tekstu. Indeksów tego typu powinniśmy użyć, gdy nasza aplikacja ma umożliwiać użytkownikom wyszukiwanie wartości, które znajdują się w tytułach, opisach i tekstach w różnych polach w kolekcji.

5. Geospatial Index: Wspiera zapytania związane z lokalizacją geograficzną. Może być używany do przechowywania i wyszukiwania danych geolokalizacyjnych, takich jak współrzędne GPS.

6. Multikey Index: Obsługuje indeksowanie tablic oraz subdokumentów. Umożliwia dzięki temu wydajne wyszukiwanie w zagnieżdżonych strukturach danych.

Tak jak było wspomniane na początku, w tym artykule nie będziemy wchodzić w szczegóły dotyczące każdego z tych typów. W kolejnych artykułach poświęconych indeksom znajdziesz więcej informacji.

Tworzenie indeksów

Jak dodać indeks w swojej kolekcji?

Tworzenie indeksów w MongoDB jest proste, możemy to zrobić za pomocą metody createIndex(). Dla przykładu utwórzmy sobie taki oparty na jednym polu, czyli Single Field Index. W konsoli Mongo wywołujemy poniższe polecenie:

db.mycollection.createIndex({ myField: 1 })

Po wykonaniu powinniśmy dostać mniej więcej taką odpowiedź w konsoli Mongo (można ona się delikatnie u Ciebie różnić, ze względu na specyfikacje Twojej kolekcji):

{ "createdCollectionAutomatically": false, "numIndexesBefore": 2, "numIndexesAfter": 3, "ok": 1 }

Co oznaczają poszczególne pola ?

1. "createdCollectionAutomatically" To pole informuje, czy kolekcja została utworzona automatycznie jako część operacji tworzenia indeksu. My otrzymaliśmy false co oznacza, że kolekcja istniała już wcześniej i nie została utworzona automatycznie.

2. "numIndexesBefore" Informacja o ilości indeksów w kolekcji przed dodaniem nowego. W tym przypadku istniały już dwa indeksy.

3. "numIndexesAfter" Pokazuje liczbę indeksów w kolekcji po dodaniu nowego. Teraz mamy już trzy indeksy.

4. "ok" Wskazuje status wykonania operacji. Mamy 1, co oznacza, że operacja zakończyła się pomyślnie.

Po wykonaniu powyższego polecenia powinniśmy już mieć utworzony indeks na polu myField w kolekcji mycollection. Wartość “1” oznacza, że jest to indeks rosnący, natomiast użycie wartości “-1” stworzyłoby indeks malejący.

Co to oznacza ?

  • indeks rosnący to indeks sortowany w porządku rosnącym. Oznacza to, że dokumenty będą uporządkowane od najmniejszej do największej wartości dla danego pola.
  • indeks malejący oznacza, że sortowanie będzie w porządku malejącym. Czyli dokumenty będą uporządkowane od największej do najmniejszej wartości dla danego pola.

Zarządzanie wydajnością indeksów

Chociaż indeksy znacząco poprawiają wydajność zapytań, ich nadmierna ilość lub niewłaściwe użycie mogą wpłynąć negatywnie na wydajność zapisu danych. Dlatego ważne jest monitorowanie i zarządzanie indeksami w sposób świadomy. Kilka najlepszych praktyk to:

1. Monitorowanie użycia indeksów: MongoDB oferuje narzędzie explain(), które pokazuje, jak zapytanie korzysta z indeksów. Analiza wyników explain() może pomóc w optymalizacji zapytań i indeksów. W kolejnych artykułach opowiemy sobie jak analizować swoje zapytanie z jego pomocą.

2. Usuwanie nieużywanych indeksów: Regularne sprawdzanie, które indeksy są używane, a które nie, pomoże w utrzymaniu optymalnej wydajności.

Jak to sprawdzić?

Można to zrobić np. za pomocą agregacji i stage’a $indexStats. Takie zapytanie mogłoby wyglądać w następujący sposób:

db.mycollection.aggregate([{$indexStats: {}}])

Jako wynik dostaniemy listę indeksów dla danej kolekcji:

[ { name: 'index1', key: { name: 1 }, host: 'host', accesses: { ops: 0, since: ISODate("2024-05-02T15:07:21.420Z") }, spec: { v: 2, key: { name: 1 }, name: 'index1' } } ]

W polu access znajduje się klucz ops, który zawiera informacje ile razy dany indeks został użyty. Dla przykładu powyżej jest to 0, co mogłoby wskazywać, że nadaje się on do likwidacji.

Nieużywane indeksy można usunąć za pomocą metody dropIndex().

3. Ostrożne dodawanie nowych indeksów: Przed dodaniem nowego indeksu warto przemyśleć jego wpływ na wydajność zapisów oraz pamięć.

Być może teraz do głowy przychodzi Ci pytanie, jak coś stworzonego do zwiększania wydajności, może równocześnie ją obniżać ?

Czym to jest spowodowane ?

Czas wszystkich operacji modyfikujących dane w kolekcji (create, update, delete) dotyczących pól z indeksami trochę się wydłuży. MongoDB będzie zmuszone zaktualizować zarówno dokument, jak i sam indeks. Mimo to, wprowadzenie indeksu zazwyczaj przynosi znaczne korzyści. Zdecydowanie większym wyzwaniem przy projektowaniu indeksów jest wybranie dla nich odpowiednich pól. Tak, żeby zysk z jego użycia był jak największy.

Jaka może być tutaj reguła kciuka ?

  • Przede wszystkim sprawdź najczęściej wykonywane zapytania, a szczególnie te, które odpowiadają np. za pobranie danych, które mają być wyświetlone na wejściu do Twojej aplikacji. Pierwsze wrażenie robi się tylko raz, więc pewnie nie chcesz, żeby użytkownik od razu po uruchomieniu musiał długo czekać.

  • Sprawdź również jakie parametry są wykorzystywane w tych zapytaniach, jeżeli jest ich kilka, warto rozważyć użycie indeksu złożonego.

  • Jeżeli jakieś zapytanie wykonuje się długo, ale jest ono rzadko używane, nie ma wpływu na User Experience, to w takim przypadku można rozważyć pominięcie indeksu.

Zakończenie

W tym wpisie zaledwie lekko poruszyliśmy temat. W kolejnych artykułach postaramy się rozwinąć treści, które tutaj poruszyliśmy. Jednak nawet z takiego krótkiego wprowadzenia można wysnuć kilka wniosków.

Indeksy są nieodzownym narzędziem w MongoDB, pozwalającym na znaczną poprawę wydajności zapytań. Ich odpowiednie zaprojektowanie i zarządzanie mogą uczynić aplikację bardziej responsywną i skalowalną. Pamiętaj jednak, że nadmiar indeksów lub ich niewłaściwe zastosowanie może przynieść odwrotny skutek. Zawsze warto analizować ich użycie i optymalizować w kontekście rzeczywistych potrzeb aplikacji.