W środowisku produkcyjnym aplikacja powinna szybko reagować na potrzeby użytkownika w celu usprawnienia interakcji użytkownika z aplikacją. Czasami jednak zapytania do bazy danych mogą zacząć się opóźniać, co powoduje dłuższe opóźnienie, zanim odpowiedź dotrze do użytkownika, lub raczej operacja przepustowości zostaje zakończona z powodu przekroczenia ustawionego średniego limitu czasu.
W tym blogu dowiemy się, jak można zidentyfikować te problemy w MongoDB, jak je naprawić, gdy tylko się pojawią, oraz jakie są możliwe strategie, które należy podjąć, aby to się nie powtórzyło.
Częściej przyczyną powolnych odpowiedzi na zapytania jest obniżona wydajność procesora, która nie jest w stanie wytrzymać podstawowego zestawu roboczego. Zestaw roboczy w tym przypadku to ilość danych i indeksów, które zostaną poddane instancji przepływności, a zatem aktywnej w tym momencie. Jest to szczególnie brane pod uwagę przy planowaniu wydajności, gdy oczekuje się, że ilość danych będzie z czasem wzrastać i liczba użytkowników korzystających z Twojej platformy.
Identyfikowanie problemu z wolnymi zapytaniami
Istnieją dwa sposoby identyfikowania powolnych zapytań w MongoDB.
- Korzystanie z Profilera
- Korzystanie z pomocnika db.currentOp()
Korzystanie z narzędzia MongoDB Profiler
Profiler bazy danych w MongoDB to mechanizm zbierania szczegółowych informacji o poleceniach bazy danych wykonywanych względem działającej instancji mongod, czyli:operacji przepustowości (Create, Read, Update i Delete) oraz poleceń konfiguracji i administracji.
Profiler używa ograniczonej kolekcji o nazwie system.profile, w której zapisuje wszystkie dane. Oznacza to, że gdy kolekcja jest pełna pod względem rozmiaru, starsze dokumenty są usuwane, aby dać miejsce na nowe dane.
Profiler jest domyślnie wyłączony, ale w zależności od poziomu profilowania można go włączyć dla bazy danych lub wystąpienia. Możliwe poziomy profilowania to:
- 0 — profiler jest wyłączony, dlatego nie zbiera żadnych danych.
- 1 - profiler zbiera dane dla operacji, które trwają dłużej niż wartość spowolnień
- 2- profiler zbiera dane dla wszystkich operacji.
Jednak włączenie profilowania ma wpływ na wydajność bazy danych i wykorzystanie dysku, zwłaszcza gdy poziom profilowania jest ustawiony na 2 . Przed włączeniem i skonfigurowaniem profilera we wdrożeniu produkcyjnym należy wziąć pod uwagę wszelkie konsekwencje wydajności.
Aby ustawić profilowanie, używamy helpera db.setProfilingLevel() takiego jak:
db.setProfilingLevel(2)
Przykładowy dokument, który będzie przechowywany w kolekcji system.profile to:
{ "was" : 0, "slowms" : 100, "sampleRate" : 1.0, "ok" : 1 }
Para klucz-wartość „ok”:1 wskazuje, że operacja się powiodła, podczas gdy slowms jest progiem czasu w milisekundach, jaki powinna zająć operacja, i domyślnie wynosi 100 ms.
Aby zmienić tę wartość
db.setProfilingLevel(1, { slowms: 50 })
Aby wyszukać dane w przebiegu kolekcji system.profile:
db.system.profile.find().pretty()
Korzystanie z db.currentOp()helper
Ta funkcja wyświetla listę aktualnie uruchomionych zapytań z bardzo szczegółowymi informacjami, takimi jak czas ich działania. W działającej powłoce mongo uruchamiasz komentarz, na przykład:
db.currentOp({„secs_running”:{$gte:5}})
Gdzie secs_running jest strategią filtrowania, dzięki której zwrócone zostaną tylko operacje, których wykonanie zajęło więcej niż 5 sekund, zmniejszając wynik. Jest to często używane, gdy stan procesora można ocenić na 100% ze względu na niekorzystny wpływ na wydajność, jaki może mieć na bazę danych. Zmieniając wartości, dowiesz się, które zapytania trwają długo.
Zwrócone dokumenty mają następujące klucze zainteresowań:
- zapytanie :co pociąga za sobą zapytanie
- aktywny : jeśli zapytanie jest nadal w toku.
- ns :nazwa kolekcji, względem której ma zostać wykonane zapytanie
- secs_running : czas trwania zapytania w sekundach
Podświetlając, które zapytania trwają długo, zidentyfikowałeś, co obciąża procesor.
Interpretowanie wyników i rozwiązywanie problemów
Jak opisaliśmy powyżej, opóźnienie zapytania jest bardzo zależne od ilości danych, które w przeciwnym razie prowadziłyby do nieefektywnych planów wykonania. Oznacza to, że na przykład, jeśli nie używasz indeksów w swojej kolekcji i chcesz zaktualizować określone rekordy, operacja musi przejść przez wszystkie dokumenty, zamiast filtrować tylko te, które pasują do specyfikacji zapytania. Logicznie rzecz biorąc, zajmie to więcej czasu, co doprowadzi do powolnego zapytania. Możesz sprawdzić nieefektywny plan wykonania, uruchamiając:explain(‘executionStats’), który dostarcza statystyki dotyczące wydajności zapytania. Od tego momentu możesz dowiedzieć się, w jaki sposób zapytanie wykorzystuje indeks, oprócz podania wskazówki, czy indeks jest optymalny.
Jeśli pomocnik wyjaśnień powróci
{
"queryPlanner" : {
"plannerVersion" : 1,
...
"winningPlan" : {
"stage" : "COLLSCAN",
...
}
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 3,
"executionTimeMillis" : 0,
"totalKeysExamined" : 0,
"totalDocsExamined" : 10,
"executionStages" : {
"stage" : "COLLSCAN",
...
},
...
},
...
}
queryPlanner.winningPlan.stage:wartość klucza COLLSCAN wskazuje, że mongod musiał przeskanować cały dokument kolekcji w celu zidentyfikowania wyników, dlatego jest to kosztowna operacja, prowadząca do powolnych zapytań.
executionStats.totalKeysExamined:0 oznacza, że kolekcja nie wykorzystuje strategii indeksowania
Dla danego zapytania liczba zaangażowanych dokumentów powinna być bliska zeru. Jeśli liczba dokumentów jest dość duża, istnieją dwie możliwości:
- Nie używam indeksowania w kolekcji
- Korzystanie z indeksu, który nie jest optymalny.
Aby utworzyć indeks dla kolekcji, uruchom polecenie:
db.collection.createIndex( { quantity: 1 } )
Gdzie ilość jest przykładowym polem wybranym jako optymalne dla strategii indeksowania.
Jeśli chcesz dowiedzieć się więcej o indeksowaniu i jakiej strategii indeksowania użyć, zajrzyj na ten blog
Wnioski
Pogorszenie wydajności bazy danych można łatwo zobrazować przez powolne zapytania, co jest najmniejszym oczekiwaniem, z jakim oczekiwalibyśmy, że napotkają użytkownicy platformy. Można zidentyfikować powolne zapytania w MongoDB, włączając profiler i konfigurując go zgodnie z niektórymi specyfikacjami lub wykonując db.currentOp() na działającej instancji mongod.
Patrząc na parametry czasu w zwróconym wyniku, możemy określić, które zapytania są opóźnione. Po zidentyfikowaniu tych zapytań używamy pomocnika wyjaśniania dla tych zapytań, aby uzyskać więcej szczegółów, na przykład, czy zapytanie korzysta z dowolnego indeksu.
Bez indeksowania operacje stają się kosztowne, ponieważ wiele dokumentów musi zostać przeskanowanych przed zastosowaniem zmian. Przy tej porażce procesor będzie przeciążony, co spowoduje powolne zapytania i rosnące skoki procesora.
Głównym błędem prowadzącym do powolnych zapytań jest nieefektywne planowanie wykonania, które można łatwo rozwiązać za pomocą indeksu z odpowiednią kolekcją.