MongoDB
 sql >> Baza danych >  >> NoSQL >> MongoDB

Wydajność zapytań MongoDB dla ponad 5 milionów rekordów

To szuka igły w stogu siana. Potrzebowalibyśmy jakiegoś wyniku explain() dla tych zapytań, które nie działają dobrze. Niestety, nawet to rozwiązałoby problem tylko dla tego konkretnego zapytania, więc oto strategia, jak do tego podejść:

  1. Upewnij się, że to nie z powodu niewystarczającej pamięci RAM i nadmiernego stronicowania
  2. Włącz profiler bazy danych (za pomocą db.setProfilingLevel(1, timeout) gdzie limit czasu to próg dla liczby milisekund, które zajmuje zapytanie lub polecenie, wszystko, co wolniej zostanie zarejestrowane)
  3. Sprawdź wolne zapytania w db.system.profile i uruchamiaj zapytania ręcznie za pomocą explain()
  4. Spróbuj zidentyfikować powolne operacje w explain() dane wyjściowe, takie jak scanAndOrder lub duże skanowane itp.
  5. Powód dotyczący selektywności zapytania i czy możliwe jest ulepszenie zapytania za pomocą indeksu w ogóle . Jeśli nie, rozważ zablokowanie ustawienia filtra dla użytkownika końcowego lub wyświetl mu okno dialogowe z ostrzeżeniem, że operacja może być powolna.

Kluczowym problemem jest to, że najwyraźniej pozwalasz użytkownikom łączyć filtry do woli. Bez przecinania się indeksów znacznie zwiększy to liczbę wymaganych indeksów.

Ponadto ślepe rzucanie indeksem przy każdym możliwym zapytaniu jest bardzo złą strategią. Ważne jest, aby uporządkować zapytania i upewnić się, że zindeksowane pola mają wystarczającą selektywność .

Załóżmy, że masz zapytanie dla wszystkich użytkowników o statusie „aktywny” i kilka innych kryteriów. Ale z 5 milionów użytkowników 3 miliony są aktywne, a 2 miliony nie, więc ponad 5 milionów wpisów to tylko dwie różne wartości. Taki indeks zwykle nie pomaga. Lepiej najpierw wyszukać inne kryteria, a następnie przeskanować wyniki. Średnio przy zwrocie 100 dokumentów będziesz musiał zeskanować 167 dokumentów, co nie zaszkodzi zbytnio wydajności. Ale to nie jest takie proste. Jeśli podstawowym kryterium jest joined_at data użytkownika i prawdopodobieństwo, że użytkownicy przestaną korzystać z czasem, są wysokie, może się okazać, że będziesz musiał przeskanować tysiące dokumentów przed znalezieniem stu dopasowań.

Tak więc optymalizacja zależy w dużej mierze od danych (nie tylko od ich struktury , ale także same dane ), jego wewnętrzne korelacje i wzorce zapytań .

Sprawy się pogarszają, gdy dane są zbyt duże dla pamięci RAM, ponieważ wtedy posiadanie indeksu jest świetne, ale skanowanie (lub nawet po prostu zwracanie) wyników może wymagać losowego pobrania dużej ilości danych z dysku, co zajmuje dużo czasu.

Najlepszym sposobem kontrolowania tego jest ograniczenie liczby różnych typów zapytań, zabronienie zapytań dotyczących informacji o niskiej selektywności i próba uniemożliwienia przypadkowego dostępu do starych danych.

Jeśli wszystko inne zawiedzie i jeśli naprawdę potrzebujesz tak dużej elastyczności w filtrach, warto rozważyć oddzielną bazę danych wyszukiwania, która obsługuje przecięcia indeksów, pobierz stamtąd identyfikatory mongo, a następnie uzyskaj wyniki z mongo za pomocą $in . Ale to jest obarczone własnymi niebezpieczeństwami.

-- EDYTUJ --

Opublikowane przez Ciebie wyjaśnienie jest pięknym przykładem problemu ze skanowaniem pól o niskiej selektywności. Najwyraźniej istnieje wiele dokumentów dla adresu „[email protected]”. Teraz wyszukiwanie tych dokumentów i sortowanie ich malejąco według znacznika czasu jest dość szybkie, ponieważ jest obsługiwane przez indeksy o wysokiej selektywności. Niestety, ponieważ istnieją tylko dwa typy urządzeń, mongo musi zeskanować 30060 dokumentów, aby znaleźć pierwszy pasujący do „mobilnego”.

Zakładam, że jest to jakiś rodzaj śledzenia sieci, a wzorzec użycia użytkownika powoduje spowolnienie zapytania (gdyby codziennie zmieniał telefon komórkowy i sieć, zapytanie byłoby szybkie).

Przyspieszenie tego konkretnego zapytania można by wykonać za pomocą złożonego indeksu, który zawiera typ urządzenia, np. za pomocą

a) ensureIndex({'username': 1, 'userAgent.deviceType' : 1, 'timestamp' :-1})

lub

b) ensureIndex({'userAgent.deviceType' : 1, 'username' : 1, 'timestamp' :-1})

Niestety oznacza to, że zapytania takie jak find({"username" :"foo"}).sort({"timestamp" :-1}); nie można już używać tego samego indeksu, więc zgodnie z opisem, liczba indeksów będzie bardzo szybko rosła.

Obawiam się, że obecnie nie ma na to bardzo dobrego rozwiązania przy użyciu mongodb.



  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Jak mogę udostępniać kolekcje MongoDB między aplikacjami Meteor?

  2. MongoDB Object.bsonSize()

  3. Jak sprawdzić, czy pole tablicy zawiera unikalną wartość lub inną tablicę w MongoDB?

  4. MongoDB i C#:wyszukiwanie bez uwzględniania wielkości liter

  5. MongoDB na komputerze z systemem Windows 7:nie można nawiązać połączenia