Optymalizacja wydajności bazy danych to kluczowy element każdego nowoczesnego systemu bazodanowego. Pozwala ona zapewnić szybki dostęp do danych, skalowalność i wysoką dostępność aplikacji. Jednym z najpopularniejszych rozwiązań NoSQL jest MongoDB, bazująca na dokumentach baza danych. Aby w pełni wykorzystać jej możliwości, konieczna jest dogłębna znajomość sposobów optymalizacji zapytań. Poniższy poradnik przeprowadzi Cię przez najważniejsze techniki i dobre praktyki stosowane przez doświadczonych programistów MongoDB.
Profile zapytań i indeksy w MongoDB
Pierwszym krokiem do optymalizacji zapytań jest zrozumienie, w jaki sposób MongoDB obsługuje zapytania. Silnik zapytań analizuje zapytanie i na podstawie dostępnych indeksów wybiera optymalną ścieżkę dostępu do danych. Stosowanie odpowiednich indeksów pozwala znacząco przyspieszyć operacje odczytu.
Aby sprawdzić, jakie indeksy są używane, można skorzystać z wyjaśnień zapytań (explain). Pokażą one dokładny plan zapytania i indeksy wykorzystane na każdym etapie. Dzięki temu można zidentyfikować wąskie gardła i dobrać odpowiednie indeksy.
Warto też włączyć profilowanie bazy danych, aby zebrać statystyki na temat wykonywanych zapytań. Pozwoli to zobaczyć najczęściej używane zapytania, czasy ich wykonania i obciążenie serwera.
Indeksy pojedyncze i złożone
Indeksy w MongoDB można tworzyć na pojedynczych polach, jak i wielu polach naraz. Indeksy pojedyncze przyśpieszają zapytania na konkretnych polach, natomiast indeksy złożone - zapytania korzystające z wielu pól.
Indeksy unikalne
Indeksy unikalne gwarantują unikalność wartości indeksowanego pola. Dzięki nim można szybko wyszukiwać dokumenty na podstawie unikalnego identyfikatora.
Indeksy geosprzestrzenne
Jeśli aplikacja korzysta z danych lokalizacyjnych, warto zastosować specjalne indeksy geosprzestrzenne. Pozwalają one na szybkie zapytania geograficzne, np. wyszukiwanie punktów w określonym promieniu.
Optymalizacja wydajności zapytań
Oprócz doboru indeksów, istnieje wiele sposobów optymalizacji samych zapytań. Poniżej przedstawiono najlepsze praktyki stosowane przez doświadczonych programistów MongoDB.
Aggregation Pipeline
Zamiast złożonych zapytań warto stosować Aggregation Pipeline. Pozwala ona na przetwarzanie danych przy użyciu sekwencji operacji, co jest zwykle szybsze niż tradycyjne zapytania.
Projections
Należy ograniczać ilość pól zwracaną przez zapytanie tylko do niezbędnych (projections). Im mniej danych MongoDB musi zwrócić, tym szybsze będzie zapytanie.
Covered Queries
Warto używać covered queries, czyli takich zapytań, które mogą być obsłużone tylko na podstawie indeksów bez dostępu do dokumentów. Mają one najlepszą wydajność.
Czytaj więcej: Bazy danych dla programistów - jak działają i jak zacząć je wykorzystywać?
Unikanie pułapek wydajności
Niektóre podejścia programistyczne mogą drastycznie spowolnić zapytania. Oto najważniejsze pułapki, których należy unikać w MongoDB.
Nieużywanie $where
Klauzula $where wykonuje zapytanie na poziomie JavaScript, co oznacza pełne przeskanowanie kolekcji. Powinna być używana tylko w ostateczności, gdy nie da się inaczej sformułować zapytania.
Ograniczanie rozmiaru dokumentów
Dokumenty MongoDB nie powinny przekraczać kilkunastu MB, w przeciwnym razie spada wydajność. Duże obiekty warto przenieść do osobnych kolekcji i łączyć zapytaniami $lookup.
Unikanie niepotrzebnych pól
Nadmiar nieindeksowanych i rzadko używanych pól spowalnia zapytania. Należy ograniczać dokumenty tylko do niezbędnych atrybutów aplikacji.
Łączenie kolekcji i lookup

Często zachodzi potrzeba łączenia danych z wielu kolekcji. Robi się to za pomocą operatora $lookup, będącego odpowiednikiem left outer join z relacyjnych baz danych.
$lookup dla left outer join
$lookup pozwala pobrać dane z drugiej kolekcji na podstawie wartości klucza obcego. Dzięki temu można np. dołączyć dane autora do kolekcji artykułów.
Pagination i sorting results
Aby uniknąć zwrócenia setek tysięcy rekordów, warto paginować i sortować rezultaty zapytań z lookup. Pozwoli to ograniczyć ilość zwracanych danych.
Optymalizacja łączeń
Operacje łączenia kolekcji mogą być kosztowne. Należy tworzyć indeksy na kluczach łączących kolekcje i ograniczać zakres pobieranych pól.
Replikacja i sharding
Aby MongoDB zapewniała wysoką dostępność i skalowalność dla dużych obciążeń, kluczowe są mechanizmy replikacji i shardingu.
Replikacja dla wysokiej dostępności
Replikacja służy do utrzymania kopii baz danych na wielu serwerach. Pozwala to zminimalizować przestoje w przypadku awarii pojedynczego węzła.
Sharding dla skalowalności
Sharding, czyli podział bazy danych na partycje, umożliwia horyzontalne skalowanie. Dane są dzielone na shardy i rozproszone na wielu serwerach.
Strategie shardingu kolekcji
Istnieje wiele strategii shardowania - na podstawie klucza, zakresu klucza, lokalizacji geograficznej itp. Wybór odpowiedniej zależy od schematu i wymagań aplikacji.
Debugowanie i profilowanie zapytań
Aby znaleźć i naprawić nieefektywne zapytania, niezbędne są odpowiednie narzędzia debugowania i profilowania.
explain() i hint()
Metody explain() i hint() pozwalają zobaczyć plan wykonania zapytania i sprawdzić używane indeksy. Pozwalają optymalizować zapytania.
profiler() i slow query log
Profiler pozwala rejestrować statystyki wszystkich operacji, natomiast slow query log zbiera zapytania przekraczające zadany czas. Ułatwiają znajdowanie wąskich gardeł.
Przegląd narzędzi diagnostycznych
MongoDB posiada bogaty zestaw narzędzi do monitorowania wydajności, w tym MongoDB Compass, Ops Manager i Cloud Manager. Warto zapoznać się z ich możliwościami.