Lastoptimierung für ein Highload-Projekt mit ElasticSearch

Hallo Habr! Mein Name ist Maxim Vasiliev, ich arbeite als Analyst und Projektmanager bei FINCH. Heute möchte ich Ihnen sagen, wie wir mit ElasticSearch 15 Millionen Anfragen in 6 Minuten bearbeiten und die tägliche Belastung auf der Website eines unserer Kunden optimieren konnten. Leider müssen wir auf Namen verzichten, da wir die NDA haben, hoffen wir, dass der Inhalt des Artikels nicht beeinflusst wird. Lass uns gehen.

Wie das Projekt aufgebaut ist


In unserem Backend erstellen wir Services, die die Leistung von Websites und mobilen Anwendungen unseres Kunden sicherstellen. Die allgemeine Struktur ist im Diagramm zu sehen:

Bild

Dabei verarbeiten wir eine Vielzahl von Transaktionen: Käufe, Zahlungen, Vorgänge mit Benutzersalden, auf denen wir viele Protokolle speichern, und importieren und exportieren diese Daten auch in externe Systeme.

Es gibt auch umgekehrte Prozesse, wenn wir Daten von einem Kunden empfangen und an den Benutzer übertragen. Darüber hinaus gibt es noch Verfahren für die Arbeit mit Zahlungs- und Bonusprogrammen.

Kurzer Hintergrund


Anfangs haben wir PostgreSQL als einziges Data Warehouse verwendet. Die Standardvorteile für DBMS: Verfügbarkeit von Transaktionen, entwickelte Sprache für die Datenerfassung, umfassende Tools für die Integration; In Verbindung mit guter Leistung haben zufriedene Menschen unsere Bedürfnisse seit langem befriedigt.

Wir haben absolut alle Daten in Postgres gespeichert: von Transaktionen bis zu Nachrichten. Aber die Anzahl der Benutzer wuchs und damit auch die Anzahl 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 sind es 125 Millionen, 2019 sind es erneut 130 Millionen. Fügen Sie dort weitere 100 bis 200 Millionen aus der mobilen Version der Site und der mobilen Anwendung hinzu, und Sie erhalten eine große Anzahl von Anfragen.

Mit dem Wachstum des Projekts hörte Postgres auf, mit der Last fertig zu werden, wir hatten keine Zeit - es erschien eine große Anzahl verschiedener Abfragen, unter denen wir keine ausreichende Anzahl von Indizes erstellen konnten.

Wir haben festgestellt, dass andere Data Warehouses erforderlich sind, die unsere Anforderungen erfüllen und PostgreSQL entlasten. Elasticsearch und MongoDB wurden als mögliche Optionen in Betracht gezogen. Letzterer verlor in folgenden Punkten:

  1. Langsame Indizierungsgeschwindigkeit mit zunehmendem Datenvolumen in Indizes. Bei Elastic ist die Geschwindigkeit unabhängig von der Datenmenge.
  2. Keine Volltextsuche

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

Wechseln zu Elastic


1. Wir haben den Übergang mit einem Point-of-Sale-Suchdienst begonnen. Unser Kunde verfügt über insgesamt rund 70.000 Verkaufsstellen und erfordert verschiedene Arten der Suche auf der Website und in der Anwendung:

  • Textsuche nach Städtenamen
  • Geosuche in einem bestimmten Radius von einem bestimmten Punkt. Zum Beispiel, wenn ein Benutzer sehen möchte, welche Verkaufsstellen seinem Haus am nächsten liegen.
  • Suche nach einem bestimmten Quadrat - Der Benutzer zeichnet ein Quadrat auf der Karte und zeigt alle Punkte in diesem Radius an.
  • Suchen Sie nach zusätzlichen Filtern. Verkaufsstellen unterscheiden sich im Sortiment voneinander

Apropos Organisation, dann haben wir in Postgres eine Datenquelle sowohl auf der Karte als auch in den Nachrichten, und in Elastic Snapshots werden 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 dafür verwendet werden sollte.

2. Als nächstes folgte der Nachrichtenbereich. Jeden Tag erscheinen Veröffentlichungen auf der Website, damit der Benutzer nicht im Informationsfluss verloren geht. Die Daten müssen vor der Ausgabe sortiert werden. Dazu benötigen Sie eine Suche: Auf der Site können Sie nach Textübereinstimmungen suchen und gleichzeitig zusätzliche Filter verbinden, da diese ebenfalls über Elastic erstellt werden.

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

Gleichzeitig müssen die Daten in einer minimalen Zeit verarbeitet werden - Benutzer möchten nicht mehrere Tage auf ein Ergebnis warten. Sie werden solche Fristen nicht durch Postgres erreichen - wir haben häufig Sperren erhalten, und während wir alle Anfragen bearbeitet haben, konnten Benutzer nicht überprüfen, ob sie Preise erhalten haben oder nicht. Dies ist für das Unternehmen nicht sehr angenehm, daher haben wir die Verarbeitung auf Elasticsearch verlagert.

Periodizität


Jetzt werden Updates ereignisweise unter den folgenden Bedingungen konfiguriert:

  1. Kasse. Sobald Daten von einer externen Quelle zu uns kommen, starten wir sofort das Update.
  2. Nachrichten. Sobald Nachrichten auf der Website bearbeitet wurden, werden sie automatisch an Elastic gesendet.

Auch hier sind die Vorteile von Elastic zu erwähnen. In Postgres müssen Sie beim Senden einer Anfrage warten, bis alle Datensätze ehrlich verarbeitet wurden. Sie können 10.000 Datensätze an Elastic senden und sofort mit der Arbeit beginnen, ohne zu warten, bis die Datensätze auf alle Shards verteilt sind. Natürlich sehen einige Shard oder Replica die Daten möglicherweise nicht sofort, aber sehr bald wird alles verfügbar sein.

Integrationsmethoden


Es gibt zwei Möglichkeiten, sich in Elastic zu integrieren:

  1. Über den nativen Client über TCP. Der native Treiber stirbt allmählich aus: Er wird nicht mehr unterstützt, er hat eine sehr unpraktische Syntax. Daher verwenden wir es praktisch nicht und versuchen, es vollständig aufzugeben.
  2. Über eine HTTP-Schnittstelle, in der Sie sowohl JSON-Anforderungen als auch Lucene-Syntax verwenden können. Letzteres ist eine Text-Engine, die Elastic verwendet. Mit dieser Option erhalten wir die Möglichkeit, JSON-Anforderungen über HTTP zu stapeln. Dies ist die Option, die wir verwenden möchten.

Dank der HTTP-Schnittstelle können wir Bibliotheken verwenden, die eine asynchrone Implementierung des HTTP-Clients ermöglichen. Wir können Batch und die asynchrone API nutzen, die letztendlich eine hohe Leistung bietet, was in den Tagen einer großen Aktion sehr hilfreich war (mehr dazu weiter unten).

Ein paar Zahlen zum Vergleichen:

  • Speichern von Benutzern, die Preise in Postgres erhalten haben, in 20 Streams ohne Gruppierung: 460.713 Einträge 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 Datensätze in 4 Minuten

Jetzt haben wir einen HTTP-Anforderungsmanager geschrieben, der JSON als Batch / not Batch erstellt und unabhängig von der Bibliothek über einen beliebigen HTTP-Client sendet. Sie können auch wählen, ob Anforderungen synchron oder asynchron gesendet werden sollen.

In einigen Integrationen verwenden wir immer noch den offiziellen Transport-Client, aber dies ist nur eine Frage des kommenden Refactorings. Gleichzeitig wird ein benutzerdefinierter Client, der auf der Basis von Spring WebClient erstellt wurde, für die Verarbeitung verwendet.

Bild

Große Förderung


Einmal im Jahr findet eine große Kampagne für Benutzer des Projekts statt - dies ist dieselbe Hochlast, da wir zu diesem Zeitpunkt gleichzeitig mit zig Millionen Benutzern arbeiten.

Normalerweise treten Spitzenlasten in den Ferien auf, aber diese Aktion ist ein völlig anderes Niveau. Im vorletzten Jahr, am Tag der Aktion, haben wir 27.580.890 Wareneinheiten verkauft. Die Daten wurden mehr als eine halbe Stunde lang verarbeitet, was den Benutzern Unannehmlichkeiten bereitete. Die Benutzer erhielten Preise für die Teilnahme, aber es wurde klar, dass der Prozess beschleunigt werden musste.

Anfang 2019 haben wir beschlossen, ElasticSearch zu benötigen. Ein ganzes Jahr lang haben wir die Verarbeitung der empfangenen Daten in Elastic und deren Ausgabe in der API der mobilen Anwendung und Site organisiert. Infolgedessen haben wir im nächsten Jahr während der Kampagne 15 131 783 Datensätze in 6 Minuten verarbeitet.

Da wir viele Leute haben, die Waren kaufen und an der Verlosung von Werbeaktionen teilnehmen möchten, ist dies eine vorübergehende Maßnahme. Jetzt senden wir relevante Informationen an Elastic, aber in Zukunft planen wir, Archivinformationen aus den letzten Monaten als permanentes Repository an Postgres zu übertragen. Um den elastischen Index nicht zu verstopfen, der auch seine Grenzen hat.

Schlussfolgerung / Schlussfolgerungen


Im Moment haben wir alle Dienste, die wir wollten, auf Elastic übertragen und vorerst angehalten. Jetzt erstellen wir einen Index in Elastic über dem persistenten Hauptspeicher in Postgres, der die Benutzerlast übernimmt.

In Zukunft planen wir die Übertragung von Diensten, wenn wir verstehen, dass die Datenanforderung zu vielfältig wird und von einer unbegrenzten Anzahl von Spalten durchsucht wird. Dies ist für Postgres keine Aufgabe mehr.

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

⌘⌘⌘


Danke fürs Lesen. Wenn Ihr Unternehmen auch ElasticSearch verwendet und über eigene Implementierungsfälle verfügt, teilen Sie uns dies mit. Es wird interessant sein zu wissen, wie andere haben :-)

All Articles