fbpx

Elasticsearch jako warstwa cache

Architektura systemów informatycznych jest wyjątkowo rozbudowaną dziedziną. Powodem takiego stanu rzeczy jest fakt, iż zazwyczaj nie istnieje jedno najlepsze podejście rozwiązujące dane zagadnienie. Nawet jeśli uda nam się dość precyzyjnie określić stojący przed nami problem, wciąż pozostaje wiele ścieżek osiągnięcia celu. Nie możemy także zapomnieć o elementach, które podlegają zmianom, przez co trudno przewidzieć wszystkie wyzwania przed jakimi dzisiejsza architektura stanie za jakiś czas.

Ważną składową każdego systemu informatycznego jest sposób w jaki przechowujemy dane. W przeważającej większości mówimy wtedy o różnych rodzajach baz danych, których specyfika definiuje miejsce ich użycia w systemie.

Dzisiaj porozmawiamy o silniku wyszukiwania Elasticsearch (https://pl.wikipedia.org/wiki/Elasticsearch). W skrócie, jest to nierelacyjna baza danych, świetnie przeszukująca treści dokumentów zapisanych w formacie JSON (https://pl.wikipedia.org/wiki/JSON). Z jednej strony pozwala to umieścić w niej niemal dowolny tekst, z drugiej bardzo szybko wyszukiwać konkretne frazy, posługując się tak zwanym indeksem. Zatem jeśli ktoś myśli o serwisie implementującym wydajną wyszukiwarkę treści operującą na dużym zbiorze tekstów, na przykład artykułów, to powinien wziąć pod uwagę Elasticsearch. Jest to główne zastosowanie owego silnika wyszukiwania, często określane jako wyszukiwanie pełnotekstowe (ang. full-text search (https://en.wikipedia.org/wiki/Full-text_search)).

Wspomniana baza danych ma jednak również mniej oczywiste zastosowanie. Okazuje się bowiem, że mechanizmy w niej zawarte, które tak sprawnie przeszukują tekst, mają inną praktyczną własność. Jest nią rozbudowany system algorytmów pamięci podręcznej (https://pl.wikipedia.org/wiki/Pami%C4%99%C4%87_podr%C4%99czna) (ang. cache) działających na wielu poziomach wyszukiwania. Sprawiają one, że Elasticsearch jest bardzo wydajny w znajdowaniu optymalnych wyników w szerokim spektrum danych. W konsekwencji nie mówimy już tylko o przeszukiwaniu tekstu, ale o szukaniu, filtrowaniu i sortowaniu niemal dowolnych elementów, które zapiszemy w bazie. Należy w tym miejscu zaznaczyć, że jest to rozwiązanie, które sprawdzi się wyśmienicie kiedy w grę wchodzi duża liczba potencjalnych użytkowników.

Dwie strony architektury

Kiedy mówimy o bazach danych zazwyczaj możemy wydzielić dwa przypadki użycia, które mają swoją specyfikę. Po pierwsze dane służą nam do tego, aby na nich pracować, czyli przechowywać w ustrukturyzowany sposób i na podstawie logiki biznesowej odpowiednio je aktualizować. Po wtóre, jeśli mowa o serwisie internetowym, zazwyczaj chcemy dane zaprezentować szerszemu gronu odbiorców, którzy w przeważającej części zainteresowani będą tylko ich odczytem.

Oczywiście obie te funkcje może pełnić jedna baza danych, jednak zastanówmy się teraz jakie cechy są istotne dla każdej z powyższych sytuacji.

Zacznijmy od zbierania i zarządzania danymi. Na tym etapie nie myślimy o skali użytkowników chcących je przeczytać, ale o tym jak możemy je utrzymywać w dobrym porządku.

Do takich celów idealnie nadają się bazy danych typu RDBMS (https://pl.wikipedia.org/wiki/System_zarz%C4%85dzania_relacyjn%C4%85_baz%C4%85_danych), czyli popularne relacyjne bazy danych, np. Postgres (https://pl.wikipedia.org/wiki/PostgreSQL). Ich zaletą jest to, że dane przechowujemy w ustrukturyzowany sposób, w powiązanych tabelach, dzięki czemu możemy prowadzić ich szczegółową analizę. Dane możemy dowolnie łączyć i porównywać ze sobą, co znacznie ułatwia implementację logiki biznesowej uzupełniającej i modyfikującej poszczególne wpisy.

Kolejnym istotnym elementem jest to, że bazy relacyjne najczęściej operują na jednym “źródle prawdy”, czyli jedno miejsce odpowiada za ich spójność i dostępność. Dzięki temu nie zdarzy się sytuacja, w której ktoś otrzyma różne wyniki pytając o to samo. Takie założenia są niezwykle istotne w pracy wymagającej jednocześnie odczytu, weryfikacji i zmiany w danych.

Kolejnym przypadkiem jest udostępnienie danych szerokiej rzeszy użytkowników. W tym miejscu nasze priorytety co do danych i ich przechowywania ulegają zmianie. W pierwszej kolejności istotne są dla nas szybkość działania i dostępność danych, ponieważ chcemy zapewnić jak najlepszą jakość usług osobom korzystającym z naszego serwisu. Nie jest tak istotne, aby każdy użytkownik dokładnie w tej samej sekundzie otrzymał w pełni zsynchronizowane informacje na dany temat. Oczywiście aktualność danych jest ważna i na poziomie bazy danych mówimy o opóźnieniach najczęściej w granicach sekund, ale mając do wyboru dłuższe oczekiwanie na świeże dane, albo szybką odpowiedź, w dużej skali wybierzemy szybkość i bezpieczeństwo serwisu.

Okazuje się, że takie cechy ma właśnie baza Elasticsearch. Może zostać rozstawiona na wielu tak zwanych węzłach, dzięki czemu nie straszna jest nam duża skala, a także potencjalne awarie. Zawsze zachowujemy dostęp do danych, a mechanizmy zapamiętywania powodują, że użytkownicy mogą cieszyć się szybkim działaniem serwisu.

Indeksy kontra relacje

Skoro ustaliliśmy, że silnik wyszukiwania Elasticsearch dobrze sprawdzi się jako baza bezpośrednio odpowiadająca na zapytania użytkownika, powinniśmy poznać jej ograniczenia w tej kwestii.

Aby dane mogły zostać wyszukane Elasticsearch musi je zaindeksować, czyli zapamiętać w taki sposób, aby kolejne wyszukiwania zajmowały jak najmniej czasu. W efekcie powstaje indeks dokumentów gwarantujący, że każde nasze zapytanie o tak przygotowane wpisy będzie optymalne.

Niestety, aby zapewnić wysoką wydajność indeksy muszą podlegać istotnym ograniczeniom. Mianowicie, nie możemy ich ze sobą łączyć tak wygodnie jak dzieje się to z tabelami w bazach relacyjnych. Algorytmy pamięci podręcznej działają tylko i wyłącznie w obrębie konkretnego typu danych, czyli jednej domeny danych.

Z praktycznego punktu widzenia, aby w pełni użyć mechanizmów optymalizacyjnych działających w bazie Elasticsearch, musimy odpowiednio zbudować indeks dokumentów i pytać o konkretny rodzaj danych przez niego reprezentowany.

Dla przykładu wyobraźmy sobie ogromny zbiór danych dotyczący książek. Załóżmy, że mamy tam informacje o konkretnych tytułach, ich autorach, wydawcach, czy miejscach sprzedaży. Zatem są to cztery domeny danych, o które możemy chcieć zapytać.

Jeśli nasz system wymaga przeszukiwania różnych domen z szerokimi i dynamicznymi warunkami operującymi na powiązanych typach danych, to taką dowolność domyślnie zapewniają bazy relacyjne. Dzięki relacjom łatwo możemy łączyć tabele i pytać o dane pod każdym kątem.

Z drugiej strony, w bazie nierelacyjnej, kiedy przygotowujemy konkretny indeks dokumentów, musimy ściśle określić w jakiej formie będzie on przechowywał dane. Ten format jednoznacznie określa zakres zapytań, który będzie dostępny. Zatem jeśli tworzymy indeks książek, to nieoptymalnie będzie pytać o listę wydawców, czy miejsc sprzedaży danego egzemplarza, natomiast bardzo wydajnie będzie znaleźć tytuły o konkretnych, wcześniej ustalonych parametrach. Oczywiście możemy stworzyć indeks dla każdej domeny danych, tak aby zapewnić sobie przeszukiwanie wielu typów, jednak dla dużych zbiorów takie podejście szybko staje się niepraktyczne.

Dwie bazy

Wiemy już, że jeśli mamy do czynienia z konkretną domeną danych, którą będziemy przeszukiwać i oczekujemy wydajnej bazy, która bezpiecznie obsłuży duży ruch, to baza nierelacyjna, a w szczególności Elasticsearch, może być solidnym graczem w naszej architekturze.

Zauważyliśmy również, dla jakich przypadków użycia baza tego typu nie jest dobrym wyborem. Wprowadzanie danych, utrzymywanie ich struktury, wielowymiarowe przeszukiwania i aktualizacje, oraz różnego rodzaju analiza, wszystkie te elementy w połączeniu będą stanowiły kłopot dla bazy operującej na poszczególnych indeksach. Z drugiej strony, tego typu akcje sprawdzą się świetnie w klasycznej, relacyjnej bazie danych.

Tu dochodzimy do punktu, w którym możemy zaryzykować stwierdzenie, że różne rodzaje baz danych sprawdzają się dobrze w swoich przypadkach użycia, oczywiście przy spełnieniu odpowiednich warunków. Zatem połączenie bazy relacyjnej, na przykład Postgres, z bazą nierelacyjną, jak Elasticsearch, w jeden system może zapewnić nam korzyści płynące z obu podejść.

W takim systemie pierwszą bazę nazwiemy “źródłem prawdy”, będzie stanowiła trzon architektury i centrum logiki biznesowej. Natomiast druga, docelowa baza, będzie na podstawie pierwszej budowała i aktualizowała swój indeks. W ten sposób stanie się ona swego rodzaju warstwą cache i odpowie na bardzo konkretny lecz dość powszechny przypadek użycia, czyli zapewni optymalny odczyt danych w dużej skali, z którą baza źródłowa nie poradziłaby sobie bez wsparcia.

Największym wyzwaniem w tak skonstruowanym tandemie będzie niewątpliwie synchronizacja danych na poziomie systemu, czyli zapewnienie dobrego przepływu informacji ze źródła do bazy docelowej. Plusem jest fakt, iż dane płyną zawsze w jednym kierunku. Zastanówmy się jednak jakie są zalety powyższej architektury.

Warstwy odpowiedzialności i cache

Pierwsza sprawa to dość oczywista refleksja, że jeśli skorzystamy z dwóch baz danych to możemy wygodnie poprowadzić linię podziału pomiędzy warstwą biznesową, a warstwą użytkownika. Z jednej strony istotnie komplikuje to system, przez co nie jest to domyślna droga dla mniejszych projektów i należy dobrze przemyśleć wszelkie za i przeciw. Z drugiej, ów podział sprawia, że odpowiedzialność każdego zbioru informacji jest jasno zdefiniowana, co znacznie rozszerza możliwości naszej architektury. Rozdzielenie pozwala optymalizować różne bazy danych pod różnym kątem. Przykładowo źródłowa będzie usytuowana w miejscu gdzie mamy mocny sprzęt i ruch wewnętrzny ma do niej szybki dostęp. Baza docelowa natomiast może być rozproszona w taki sposób, aby zapewnić jak najlepszą wydajność dla ruchu zewnętrznego i odporność na awarie infrastruktury.

Druga sprawa dotyczy warstwy pamięci podręcznej (ang. cache) zapamiętującej zapytania i ich wyniki. Jest to szczególnie istotny element, kiedy rozważamy dużą liczbę użytkowników.

Okazuje się, że sposób działania Elasticsearch zapewnia nam bardzo konkretny rodzaj cache, który swojej “pamięci” używa na poziomie indeksu. Efekt jest taki, że kiedy do bazy docelowej trafia dokument to jest on automatycznie włączany do indeksu i wszystkich warstw powiązanej z nim pamięci podręcznej. W krótkim czasie po aktualizacji dokumentu wszystkie zapytania będą zwracały wyniki z nowymi danymi.

Z tego powodu nie występuje tu problem znany z innych podejść do budowania cache, na przykład na poziomie endpointów zwracających dane klientowi. W tych przypadkach odświeżenie zapamiętanych informacji wymaga odczekania znacznego czasu, na który opiewa cache.

Te dwa elementy stanowią o ogromnej sile Elasticsearch. Możemy skonfigurować tę bazę w taki sposób, aby była zawsze dostępna dla użytkowników, a dodatkowo nie martwimy się unieważnieniem cache. Jedyne co robimy to aktualizujemy dokument, w wygodnym dla siebie momencie, a automatyka zrobi za nas resztę.

Podsumowanie

Elasticsearch zazwyczaj stoi obok baz relacyjnych ponieważ nie posiada wszystkich cech kluczowych dla bazy głównej. Jednakże, najpewniej dzięki temu skupia się on na aspektach nieosiągalnych dla tradycyjnych baz relacyjnych i to stanowi jego siłę.

Oczywiście istnieje wiele standardowych zastosowań tego typu bazy danych, jednak ze względu na swoją prostotę i wydajność Elasticsearch może z powodzeniem pełnić rolę warstwy cache. Szczególnie jeśli cenimy sobie jego mechanizmy aktualizacji indeksu po każdej zmianie dokumentu.

Wracając do początku naszych dzisiejszych rozważań, jak widać, architektura systemów informatycznych jest dość swobodna. Wiele elementów, które na pozór nie pasują do konkretnego przypadku użycia, po głębszej analizie i poznaniu okazuje się bardzo przydatnych.

Kluczem do zaprojektowania dobrego rozwiązania jest zrozumienie specyfiki poszczególnych elementów architektury, wykorzystanie ich zalet i poradzenie sobie z ich słabościami.

    Pracuj z nami

    Wyślij swoje CV już teraz lub sprawdź TUTAJ stanowiska, na które aktualnie rekrutujemy