Witajcie przyjaciele! Na naszym forum udzielają się ludzie o różnym poziomie doświadczenia - i to jest świetne! Ale teraz szukam prawdziwych tytanów Ruby!
Elasticsearch to wyszukiwarka oparta na zaufanej i dojrzałej bibliotece - Apache Lucene. Ogromna aktywność w git projekt repository and the implementation in such projects as GitHub, SoundCloud, Stack Overflow and LinkedIn bear testimony to its great popularity. The part “Elastic” says it all about the nature of the system, whose capabilities are enormous: from a simple file search on a small scale, through knowledge discovery, to big data analysis in real time.What makes Elastic a more powerful than the competition is the set of default configurations and behaviors, which allow to create a cluster and start adding documents to the index in a couple of minutes. Elastic will configure a cluster for you, will define an index and define the types of fields for the first document obtained, and when you add another server, it will automatically deal with dividing index data between servers.Unfortunately, the above mentioned automation makes it unclear to us what the default settings implicate, and it often turns out to be misleading. This article starts a series where I will be dealing with the most popular gotchas, which you might encounter during the process of Elastic-based app creation.
Liczba odłamków nie może zostać zmieniona
Zindeksujmy pierwszy dokument używając API indeksu:
$ curl -XPUT 'http://localhost:9200/myindex/employee/1' -d '{
"first_name" : "Jane",
"last_name" : "Smith",
"steet_number": 12
}'
W tym momencie Elastic tworzy dla nas indeks o nazwie myindex. To czego tutaj nie widać, to liczba shardów przypisanych do indeksu. Shardy można rozumieć jako poszczególne procesy odpowiedzialne za indeksowanie, przechowywanie i przeszukiwanie części dokumentów całego indeksu. Podczas procesu indeksowania dokumentów, elastic decyduje, w którym shardzie powinien znaleźć się dany dokument. Jest to oparte na następującym wzorze:
shard = hash(document_id) % number_of_primary_shards
Teraz jest jasne, że liczba podstawowych shardów nie może zostać zmieniona dla indeksu zawierającego dokumenty. Tak więc, przed indeksowaniem pierwszego dokumentu, zawsze należy utworzyć indeks ręcznie, podając liczbę shardów, która jest wystarczająca dla wolumenu indeksowanych danych:
$ curl -XPUT 'http://localhost:9200/myindex/' -d '{
"settings" : {
"number_of_shards" : 10
}
}'
Wartość domyślna dla number_of_shards Oznacza to, że indeks może być skalowany do maksymalnie 5 serwerów, które zbierają dane podczas indeksowania. Dla środowiska produkcyjnego wartość shardów powinna być ustawiona w zależności od oczekiwanej częstotliwości indeksacji i wielkości dokumentów. Dla środowisk deweloperskich i testowych zalecam ustawienie wartości na 1 - dlaczego? Zostanie to wyjaśnione w następnym akapicie tego artykułu.
Sortowanie wyników wyszukiwania tekstowego przy stosunkowo niewielkiej liczbie dokumentów
Gdy wyszukujemy dokument za pomocą frazy:
$ curl -XGET 'http://localhost:9200/myindex/my_type/_search' -d
'{
"query": {
"match": {
"title": "The quick brown fox"
}
}
}'
Elastic przetwarza wyszukiwanie tekstu w kilku krokach:
- fraza z zapytania jest konwertowana do identycznej postaci, w jakiej dokument został zindeksowany, w naszym przypadku będzie to zestaw terminów:
["szybki", "brązowy", "lis"]. ("the" zostało usunięte, ponieważ jest nieistotne),
- indeks jest przeglądany w celu wyszukania dokumentów zawierających co najmniej jedno z wyszukiwanych słów,
- Każdy pasujący dokument jest oceniany pod kątem dopasowania do wyszukiwanej frazy,
- Wyniki są sortowane według obliczonej trafności, a pierwsza strona wyników jest zwracana użytkownikowi.
W trzecim kroku brane są pod uwagę między innymi następujące wartości:
- ile słów z wyszukiwanej frazy znajduje się w dokumencie
- jak często dane słowo występuje w dokumencie (TF - term frequency)
- czy i jak często pasujące słowa występują w innych dokumentach (IDF - inverse document frequency) - im bardziej popularne słowo w innych dokumentach, tym mniej znaczące
- Jak długi jest dokument
Funkcjonowanie IDF jest dla nas ważne. Elastic ze względów wydajnościowych nie oblicza tej wartości dla każdego dokumentu w indeksie - zamiast tego każdy shard (index worker) oblicza swój lokalny IDF i wykorzystuje go do sortowania. Dlatego podczas przeszukiwania indeksu przy małej liczbie dokumentów możemy uzyskać znacząco różne wyniki w zależności od liczby shardów w indeksie i rozkładu dokumentów.
Wyobraźmy sobie, że mamy 2 shardy w indeksie; w pierwszym znajduje się 8 dokumentów indeksowanych słowem "fox", a w drugim tylko 2 dokumenty z tym samym słowem. W rezultacie słowo "fox" będzie się znacznie różnić w obu shardach, co może prowadzić do błędnych wyników. Dlatego do celów rozwojowych należy utworzyć indeks składający się tylko z jednego głównego shardu:
$ curl -XPUT 'http://localhost:9200/myindex/' -d
'{ "settings" : { "number_of_shards" : 1 } }'
Wyświetlanie wyników "odległych" stron wyszukiwania zabija klaster
Jak już pisałem w poprzednich akapitach, dokumenty w indeksie są współdzielone pomiędzy całkowicie indywidualnymi procesami indeksu - shardami. Każdy proces jest całkowicie niezależny i zajmuje się tylko dokumentami, które są do niego przypisane.
Gdy przeszukujemy indeks z milionami dokumentów i czekamy na 10 najlepszych wyników, każdy shard musi zwrócić 10 najlepiej dopasowanych wyników do klastra węzełktóry zainicjował wyszukiwanie. Następnie odpowiedzi z każdego shardu są łączone i wybieranych jest 10 najlepszych wyników wyszukiwania (w ramach całego indeksu). Takie podejście pozwala efektywnie rozdzielić proces wyszukiwania pomiędzy wiele serwerów.
Wyobraźmy sobie, że nasza aplikacja pozwala na przeglądanie 50 wyników na stronę, bez ograniczeń dotyczących liczby stron, które mogą być przeglądane przez użytkownika. Pamiętajmy, że nasz indeks składa się z 10 podstawowych shardów (po 1 na serwer).
Zobaczmy, jak będzie wyglądać pozyskiwanie wyników wyszukiwania dla pierwszej i setnej strony:
Strona nr 1 wyników wyszukiwania:
- Węzeł odbierający zapytanie (kontroler) przekazuje je do 10 shardów.
- Każdy shard zwraca 50 najlepiej dopasowanych dokumentów posortowanych według trafności.
- Po otrzymaniu odpowiedzi z każdego shardu, kontroler scala wyniki (500 dokumentów).
- Nasze wyniki to 50 najlepszych dokumentów z poprzedniego kroku.
Strona nr 100 wyników wyszukiwania:
- Węzeł odbierający zapytanie (kontroler) przekazuje je do 10 shardów.
- Każdy shard zwraca 5000 najlepiej dopasowanych dokumentów posortowanych według trafności.
- Po otrzymaniu odpowiedzi z każdego shardu, kontroler scala wyniki (50000 dokumentów).
- Nasze wyniki to dokumenty z poprzedniego kroku umieszczone na pozycjach 4901 - 5000.
Zakładając, że jeden dokument ma rozmiar 1 KB, w drugim przypadku oznacza to, że ~50 MB danych musi zostać przesłanych i przetworzonych w klastrze, aby wyświetlić 100 wyników dla jednego użytkownika.
Nietrudno zauważyć, że ruch sieciowy i obciążenie indeksu znacznie wzrasta z każdą kolejną stroną wyników. Dlatego też nie zaleca się udostępniania użytkownikowi "dalekich" stron wyszukiwania. Jeśli nasz indeks jest dobrze skonfigurowany, to użytkownik powinien znaleźć interesujący go wynik na pierwszych stronach wyszukiwania, a my uchronimy się przed niepotrzebnym obciążeniem naszego klastra. Aby udowodnić tę regułę, sprawdź, do jakiej liczby stron wyników wyszukiwania pozwalają przeglądać najpopularniejsze wyszukiwarki internetowe.
Interesująca jest również obserwacja czasu odpowiedzi przeglądarki dla kolejnych stron wyników wyszukiwania. Na przykład, poniżej można znaleźć czasy odpowiedzi dla poszczególnych stron wyników wyszukiwania w wyszukiwarce Google (wyszukiwaną frazą była "wyszukiwarka"):
| Strona wyników wyszukiwania (10 dokumentów na stronie) | Czas odpowiedzi |
|——————————————–|—————|
| 1 | 250ms |
| 10 | 290ms |
| 20 | 350ms |
| 30 | 380ms |
| 38 (ostatni dostępny) | |
W następnej części przyjrzę się bliżej problemom związanym z indeksowaniem dokumentów.