Ladeoptimierung für ein Highload-Projekt mit ElasticSearch

Hey Habr! Mein Name ist Maxim Vasiliev, ich arbeite als Analyst und Projektmanager bei FINCH. Heute möchte ich Ihnen erzählen, wie wir mit ElasticSearch 15 Millionen Anfragen in 6 Minuten bearbeiten und die tägliche Auslastung der Website eines unserer Kunden optimieren konnten. Auf Namen müssen wir leider verzichten, da wir eine NDA haben, wir hoffen, dass der Inhalt des Artikels dadurch nicht leidet. Lass uns gehen.

Wie das Projekt funktioniert

Auf unserem Backend erstellen wir Dienste, die die Leistung der Websites und mobilen Anwendungen unserer Kunden sicherstellen. Der allgemeine Aufbau ist im Diagramm zu sehen:

Ladeoptimierung für ein Highload-Projekt mit ElasticSearch

Im Laufe der Arbeit verarbeiten wir eine Vielzahl von Transaktionen: Käufe, Zahlungen, Vorgänge mit Benutzersalden, für die wir viele Protokolle speichern, sowie den Import und Export dieser Daten in externe Systeme.

Es gibt auch umgekehrte Prozesse, wenn wir Daten vom Client erhalten und an den Benutzer übertragen. Darüber hinaus gibt es noch Prozesse für die Arbeit mit Zahlungen und Bonusprogrammen.

Kurzer Hintergrund

Zunächst verwendeten wir PostgreSQL als einzigen Datenspeicher. Seine Standardvorteile für ein DBMS: das Vorhandensein von Transaktionen, eine entwickelte Daten-Sampling-Sprache, eine breite Palette von Tools zur Integration; gepaart mit guter Leistung hat unsere Bedürfnisse lange Zeit befriedigt.

Wir haben absolut alle Daten in Postgres gespeichert: von Transaktionen bis hin zu Nachrichten. Doch die Zahl der Nutzer wuchs und damit auch die Zahl der Anfragen.

Zum Verständnis beträgt die jährliche Anzahl der Sitzungen im Jahr 2017 allein auf der Desktop-Site 131 Millionen. Im Jahr 2018 - 125 Millionen. 2019 erneut 130 Millionen. Fügen Sie weitere 100-200 Millionen aus der mobilen Version der Site und der mobilen Anwendung hinzu, und Sie wird eine große Anzahl von Anfragen erhalten.

Mit dem Wachstum des Projekts kam Postgres nicht mehr mit der Last zurecht, wir hatten keine Zeit – es erschien eine große Anzahl verschiedener Abfragen, für die wir nicht genügend Indizes erstellen konnten.

Uns war klar, dass Bedarf an anderen Datenspeichern bestand, die unsere Anforderungen erfüllen und PostgreSQL entlasten würden. Als mögliche Optionen wurden Elasticsearch und MongoDB in Betracht gezogen. Letzterer verlor in folgenden Punkten:

  1. Langsame Indizierungsgeschwindigkeit, wenn die Datenmenge in den Indizes wächst. Bei Elastic ist die Geschwindigkeit nicht von der Datenmenge abhängig.
  2. Keine Volltextsuche

Also haben wir uns für Elastic entschieden und uns auf den Übergang vorbereitet.

Übergang zu Elastic

1. Wir haben mit der Umstellung vom Point-of-Sale-Suchdienst begonnen. Unser Kunde verfügt über insgesamt etwa 70 Verkaufsstellen. Dies erfordert mehrere Suchvorgänge auf der Website und in der Anwendung:

  • Textsuche nach Städtenamen
  • Geosuche innerhalb eines bestimmten Radius von einem bestimmten Punkt aus. Zum Beispiel, wenn der Benutzer sehen möchte, welche Verkaufsstellen seinem Zuhause am nächsten liegen.
  • Suche nach einem bestimmten Quadrat – der Benutzer zeichnet ein Quadrat auf der Karte und alle Punkte in diesem Radius werden ihm angezeigt.
  • Suche nach zusätzlichen Filtern. Die Verkaufsstellen unterscheiden sich im Sortiment voneinander

Wenn wir über die Organisation sprechen, dann haben wir in Postgres eine Datenquelle sowohl für die Karte als auch für die Nachrichten, und in Elastic werden Snapshots aus den Originaldaten erstellt. Tatsache ist, dass Postgres die Suche zunächst nicht nach allen Kriterien bewältigen konnte. Es gab nicht nur viele Indizes, sie konnten sich auch überschneiden, sodass der Postgres-Scheduler verloren ging und nicht verstand, welcher Index verwendet werden sollte.

2. Als nächstes folgte der Nachrichtenbereich. Damit sich der Nutzer nicht im Informationsfluss verliert, erscheinen täglich Veröffentlichungen auf der Seite, die Daten müssen vor der Veröffentlichung sortiert werden. Genau dafür ist die Suche gedacht: Sie können die Site nach Textübereinstimmungen durchsuchen und gleichzeitig zusätzliche Filter anschließen, da diese ebenfalls über Elastic erstellt werden.

3. Dann haben wir die Transaktionsverarbeitung verschoben. Benutzer können auf der Website ein bestimmtes Produkt kaufen und an einer Verlosung teilnehmen. Nach solchen Käufen verarbeiten wir insbesondere an Wochenenden und Feiertagen eine große Datenmenge. Zum Vergleich: Wenn an normalen Tagen die Zahl der Käufe zwischen 1,5 und 2 Millionen liegt, kann sie an Feiertagen bis zu 53 Millionen betragen.

Gleichzeitig müssen die Daten in kürzester Zeit verarbeitet werden – Nutzer warten nicht gerne mehrere Tage auf das Ergebnis. Es gibt keine Möglichkeit, solche Fristen über Postgres einzuhalten – wir erhielten häufig Sperren und während wir alle Anfragen bearbeiteten, konnten Benutzer nicht überprüfen, ob sie Preise erhalten hatten oder nicht. Das ist für das Geschäft nicht sehr angenehm, daher haben wir die Verarbeitung auf Elasticsearch verlagert.

Periodizität

Jetzt werden Updates ereignisbasiert konfiguriert, entsprechend den folgenden Bedingungen:

  1. Verkaufsstellen. Sobald wir Daten von einer externen Quelle erhalten, beginnen wir sofort mit der Aktualisierung.
  2. Nachricht. Sobald eine Nachricht auf der Website bearbeitet wird, wird sie automatisch an Elastic gesendet.

Auch hier sind die Vorteile von Elastic zu erwähnen. Wenn Sie in Postgres eine Anfrage senden, müssen Sie warten, bis alle Datensätze ehrlich verarbeitet werden. Sie können 10 Datensätze an Elastic senden und sofort mit der Arbeit beginnen, ohne darauf warten zu müssen, dass die Datensätze auf alle Shards verteilt werden. Natürlich sehen einige Shards oder Replikate die Daten möglicherweise nicht sofort, aber alles wird sehr bald verfügbar sein.

Integrationsmethoden

Es gibt zwei Möglichkeiten zur Integration mit Elastic:

  1. Über einen nativen Client über TCP. Der native Treiber stirbt allmählich aus: Er wird nicht mehr unterstützt, er hat eine sehr unpraktische Syntax. Deshalb nutzen wir es praktisch nicht und versuchen, ganz darauf zu verzichten.
  2. Über eine HTTP-Schnittstelle, die sowohl JSON-Anfragen als auch Lucene-Syntax verwenden kann. Die letzte ist eine Text-Engine, die Elastic verwendet. In dieser Version erhalten wir die Möglichkeit, JSON-Anfragen über HTTP stapelweise durchzuführen. Dies ist die Option, die wir nutzen möchten.

Dank der HTTP-Schnittstelle können wir Bibliotheken verwenden, die eine asynchrone Implementierung des HTTP-Clients bereitstellen. Wir können Batch und die asynchrone API nutzen, was zu einer hohen Leistung führt, was in den Tagen der großen Werbeaktion sehr hilfreich war (mehr dazu weiter unten).

Einige Zahlen zum Vergleich:

  • Speichern von Postgres-Bounty-Benutzern in 20 Threads ohne Gruppierung: 460713 Datensätze in 42 Sekunden
  • Elastischer + reaktiver Client für 10 Threads + Batch für 1000 Elemente: 596749 Datensätze in 11 Sekunden
  • Elastischer + reaktiver Client für 10 Threads + Batch für 1000 Elemente: 23801684 Einträge in 4 Minuten

Jetzt haben wir einen HTTP-Request-Manager geschrieben, der JSON als Batch/nicht Batch erstellt und über jeden HTTP-Client sendet, unabhängig von der Bibliothek. Sie können Anfragen auch synchron oder asynchron senden.

Bei einigen Integrationen verwenden wir immer noch den offiziellen Transport-Client, aber das ist nur eine Frage des nächsten Refactorings. In diesem Fall wird für die Verarbeitung ein benutzerdefinierter Client verwendet, der auf Basis von Spring WebClient erstellt wurde.

Ladeoptimierung für ein Highload-Projekt mit ElasticSearch

große Promotion

Einmal im Jahr veranstaltet das Projekt eine große Aktion für Benutzer – das ist das gleiche Highload, da wir zu dieser Zeit mit zig Millionen Benutzern gleichzeitig arbeiten.

Normalerweise kommt es in den Ferien zu Belastungsspitzen, doch diese Aktion ist auf einem ganz anderen Niveau. Im vorletzten Jahr haben wir am Tag der Aktion 27 Wareneinheiten verkauft. Die Datenverarbeitung dauerte mehr als eine halbe Stunde, was zu Unannehmlichkeiten für die Benutzer führte. Für die Teilnahme erhielten die Nutzer Preise, doch es wurde klar, dass der Prozess beschleunigt werden musste.

Anfang 2019 entschieden wir, dass wir ElasticSearch brauchten. Ein ganzes Jahr lang haben wir die Verarbeitung der empfangenen Daten in Elastic und deren Ausgabe in der API der mobilen Anwendung und Website organisiert. Infolgedessen haben wir im nächsten Jahr während der Kampagne verarbeitet 15 Einträge in 131 Minuten.

Da wir viele Menschen haben, die Waren kaufen und an der Verlosung von Preisen im Rahmen von Werbeaktionen teilnehmen möchten, handelt es sich hierbei um eine vorübergehende Maßnahme. Jetzt senden wir aktuelle Informationen an Elastic, planen aber, in Zukunft archivierte Informationen der letzten Monate als dauerhaften Speicher an Postgres zu übertragen. Um den Elastic-Index nicht zu verstopfen, hat dieser auch seine Grenzen.

Fazit/Schlussfolgerungen

Im Moment haben wir alle Dienste, die wir wollten, auf Elastic übertragen und damit vorerst pausiert. Jetzt bauen wir in Elastic einen Index auf dem Hauptspeicher in Postgres auf, der die Benutzerlast übernimmt.

In Zukunft planen wir, Dienste zu übertragen, wenn wir feststellen, dass die Datenanfrage zu vielfältig wird und nach einer unbegrenzten Anzahl von Spalten durchsucht wird. Dies ist keine Aufgabe mehr für Postgres.

Wenn wir eine Volltextsuche in der Funktionalität benötigen oder viele verschiedene Suchkriterien haben, dann wissen wir bereits, dass dies in Elastic übersetzt werden muss.

⌘⌘⌘

Danke fürs Lesen. Wenn Ihr Unternehmen ebenfalls ElasticSearch nutzt und eigene Implementierungsfälle hat, dann sagen Sie es uns. Es wird interessant sein zu erfahren, wie es anderen geht 🙂

Source: habr.com

Kommentar hinzufügen