Optymalizacja obciążenia w projekcie Highload za pomocą ElasticSearch

Cześć, Habr! Nazywam się Maksym Wasiljew, pracuję jako analityk i menedżer projektów w FINCH. Dziś chciałbym opowiedzieć, jak dzięki ElasticSearch udało nam się przetworzyć 15 mln zapytań w ciągu 6 minut i zoptymalizować codzienne obciążenia na stronie jednego z naszych klientów. Niestety, musimy obejść się bez imion, ponieważ mamy NDA, mam nadzieję, że to nie wpłynie na treść artykułu. Zaczynajmy.

Jak działa projekt

W naszym backendzie tworzymy usługi, które zapewniają funkcjonowanie stron internetowych i aplikacji mobilnej naszego klienta. Ogólną strukturę można zobaczyć na schemacie:

Optymalizacja obciążenia w projekcie Highload za pomocą ElasticSearch

W trakcie pracy przetwarzamy dużą ilość transakcji: zakupów, wypłat, operacji z saldami użytkowników, dla których przechowujemy wiele logów, a także importujemy i eksportujemy te dane do zewnętrznych systemów.

Odbieramy także dane od klienta i przekazujemy je użytkownikom. Ponadto istnieją również procesy związane z płatnościami i programami lojalnościowymi.

Krótka historia

Początkowo jako jedyne miejsce przechowywania danych używaliśmy PostgreSQL. Jego standardowe zalety dla DBMS: istnienie transakcji, rozwinięty język zapytań, bogate narzędzia do integracji; w połączeniu z dobrą wydajnością długo zaspokajały nasze potrzeby.

Przechowywaliśmy w Postgres wszystko: od transakcji po wiadomości. Jednak liczba użytkowników rosła, a wraz z nią liczba zapytań.

Dla porównania, w 2017 roku roczna liczba sesji tylko na wersji desktopowej wynosiła 131 mln. W 2018 roku — 125 mln. W 2019 roku znowu 130 mln. Dodajcie do tego jeszcze 100-200 mln z mobilnej wersji strony oraz aplikacji mobilnej, a otrzymacie kolosalną ilość zapytań.

Wraz ze wzrostem projektu, Postgres przestał radzić sobie z obciążeniem, nie nadążaliśmy — pojawiło się wiele różnorodnych zapytań, dla których nie mogliśmy stworzyć wystarczającej liczby indeksów.

Rozumieliśmy, że istnieje potrzeba innych baz danych, które mogłyby zaspokoić nasze potrzeby i odciążyć PostgreSQL. Jako możliwe opcje rozważaliśmy Elasticsearch i MongoDB. Ta ostatnia miała kilka wad:

  1. Wolne tempo indeksowania w miarę wzrostu objętości danych w indeksach. W przypadku Elastic prędkość nie zależy od objętości danych.
  2. Brak wyszukiwania pełnotekstowego

Zdecydowaliśmy się na Elastic i przygotowaliśmy się do migracji.

Migracja do Elastic

1. Rozpoczęliśmy migrację od usługi wyszukiwania punktów sprzedaży. Nasz klient ma w sumie około 70 000 punktów sprzedaży, a jednocześnie wymaga kilku typów wyszukiwania na stronie i w aplikacji:

  • Wyszukiwanie tekstowe według nazwy miejscowości
  • Wyszukiwanie geograficzne w zadanym promieniu od określonego punktu. Na przykład, jeśli użytkownik chce zobaczyć, które punkty sprzedaży są najbliżej jego domu.
  • Wyszukiwanie w zadanym kwadracie – użytkownik zaznacza kwadrat na mapie, a system pokazuje wszystkie punkty w tym obszarze.
  • Wyszukiwanie według dodatkowych filtrów. Punkty sprzedaży różnią się od siebie asortymentem.

Jeśli chodzi o organizację, to w Postgres mamy źródło danych zarówno dotyczące mapy, jak i wiadomości, a w Elastic tworzone są Snapshoty z danych oryginalnych. Rzecz w tym, że początkowo Postgres nie radził sobie z wyszukiwaniem według wszystkich kryteriów. Poza tym, że było wiele indeksów, mogły one również zachodzić na siebie, dlatego planista Postgres gubił się i nie rozumiał, który indeks powinien użyć.

2. Następną do migracji był dział wiadomości. Na stronie każdego dnia pojawiają się publikacje, aby użytkownik nie zgubił się w strumieniu informacji, dane muszą być sortowane przed wydaniem. W tym celu potrzebne jest wyszukiwanie: na stronie można szukać według dopasowania tekstowego, a jednocześnie podłączać dodatkowe filtry, ponieważ są one również zrealizowane przez Elastic.

3. Następnie przenieśliśmy przetwarzanie transakcji. Użytkownicy mogą kupować określone towary na stronie i brać udział w losowaniach nagród. Po takich zakupach przetwarzamy dużą ilość danych, szczególnie w weekendy i święta. Dla porównania, jeśli w zwykłe dni liczba zakupów wynosi około 1,5-2 mln, to w święta może osiągnąć 53 mln.

Jednocześnie dane muszą być przetwarzane w jak najkrótszym czasie — użytkownicy nie lubią czekać na wyniki przez kilka dni. Przez Postgres takich terminów nie da się osiągnąć — często mieliśmy zablokowania, a podczas przetwarzania wszystkich zapytań użytkownicy nie mogli sprawdzić, czy otrzymali nagrody, czy nie. To nie jest przyjemne dla biznesu, dlatego przenieśliśmy przetwarzanie do Elasticsearch.

Częstotliwość

Aktualizacje są obecnie ustawione na zdarzenie, według następujących warunków:

  1. Punkty sprzedaży. Gdy tylko otrzymujemy dane z zewnętrznego źródła, natychmiast uruchamiamy aktualizację.
  2. Aktualności. Gdy tylko na stronie jest edytowana jakakolwiek wiadomość, automatycznie wysyła się ona do Elastic.

Raz jeszcze warto wspomnieć o zaletach Elastic. W Postgresie podczas wysyłania zapytania trzeba czekać, aż uczciwie przetworzy wszystkie zapisane dane. W Elastic można wysłać 10 tys. wpisów i od razu zacząć pracować, nie czekając, aż dane rozprzestrzenią się po wszystkich Shardach. Oczywiście jakiś Shard lub Replica mogą nie zobaczyć danych od razu, ale bardzo szybko wszystko będzie dostępne.

Sposoby integracji

Są dwa sposoby integracji z Elastic:

  1. Przez natywny klient za pomocą TCP. Natywny sterownik powoli się wygasza: przestaje być wspierany, ma także bardzo niewygodną składnię. Dlatego praktycznie go nie używamy i staramy się całkowicie od niego odstąpić.
  2. Przez interfejs HTTP, w którym można używać zarówno zapytań JSON, jak i składni Lucene. To ostatnie to silnik tekstowy, który wykorzystuje Elastic. W tej opcji uzyskujemy możliwość Batch przez zapytania JSON za pomocą HTTP. To właśnie tę opcję staramy się wykorzystać.

Dzięki interfejsowi HTTP możemy korzystać z bibliotek, które oferują asynchroniczną implementację klienta HTTP. Możemy wykorzystać przewagę Batch i asynchronicznego API, co ostatecznie zapewnia wysoką wydajność, która bardzo pomogła w dniach dużej akcji (o tym poniżej)

Trochę danych do porównania:

  • Zachowanie użytkowników, którzy otrzymali nagrody w Postgresie w 20 strumieniach bez grupowania: 460713 wpisów w 42 sekundy
  • Elastic + klient reaktywny w 10 strumieniach + batch na 1000 elementów: 596749 wpisów w 11 sekund
  • Elastic + klient reaktywny w 10 strumieniach + batch na 1000 elementów: 23801684 wpisów w 4 minuty

Obecnie napisaliśmy menedżera zapytań po HTTP, który buduje JSON, jako Batch/nie Batch i wysyła przez dowolnego klienta HTTP, niezależnie od biblioteki. Można również wybierać, czy wysyłać zapytania synchronicznie czy asynchronicznie.

W niektórych integracjach wciąż korzystamy z oficjalnego transport clienta, ale to tylko kwestia najbliższego refaktoryzacji. Przy tym do przetwarzania używany jest własny klient zbudowany na bazie Spring WebClient.

Optymalizacja obciążenia w projekcie Highload za pomocą ElasticSearch

Duża akcja

Raz w roku na projekcie odbywa się duża akcja dla użytkowników — to ten sam Highload, ponieważ w tym czasie pracujemy z dziesiątkami milionów użytkowników jednocześnie.

Zwykle szczyty obciążenia występują w dni świąteczne, ale ta akcja to zupełnie inny poziom. W roku przedostatnim, w dniu akcji, sprzedaliśmy 27 580 890 sztuk towaru. Dane były przetwarzane przez ponad pół godziny, co powodowało niedogodności dla użytkowników. Użytkownicy otrzymali nagrody za udział, ale stało się jasne, że proces należy przyspieszyć.

Na początku 2019 roku postanowiliśmy, że potrzebujemy ElasticSearch. Przez cały rok organizowaliśmy przetwarzanie otrzymywanych danych w Elastic i ich wydanie w API mobilnej aplikacji i stronie internetowej. W rezultacie w następnym roku podczas akcji przetworzyliśmy 15 131 783 rekordów w 6 minut.

Ponieważ mamy wielu chętnych na zakup towaru i udział w losowaniu nagród podczas akcji, to jest to rozwiązanie tymczasowe. Aktualnie wysyłamy bieżące informacje do Elastic, ale w przyszłości planujemy przenieść archiwalne dane z poprzednich miesięcy do Postgres jako stałego magazynu. Aby nie zanieczyszczać indeksu Elastic, który również ma swoje ograniczenia.

Podsumowanie/Wnioski

Na obecną chwilę przenieśliśmy do Elastic wszystkie usługi, które chcieliśmy, i na tym chwilowo zrobiliśmy przerwę. Obecnie budujemy indeks w Elastic na podstawowym trwałym magazynie w Postgres, który przyjmuje obciążenia użytkowników.

W przyszłości planujemy przenosić usługi, jeśli zrozumiemy, że zapytania o dane stają się zbyt różnorodne i są wyszukiwane po nieograniczonej liczbie kolumn. To już zadanie nie dla Postgres.

Jeśli będziemy potrzebować wyszukiwania pełnotekstowego w funkcjonalności lub jeśli pojawi się wiele różnorodnych kryteriów wyszukiwania, to już wiemy, że trzeba to przenieść do Elastic.

⌘⌘⌘

Dziękuję za przeczytanie. Jeśli w Waszej firmie również używa się ElasticSearch i macie własne przypadki wdrożeń, to podzielcie się nimi. Będzie ciekawie dowiedzieć się, jak to robią inni 🙂

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster