Hallo, Хабр! Mein Name ist Maxim Vasiliev, ich arbeite als Analyst und Projektmanager bei FINCH. Heute möchte ich Ihnen erzählen, wie wir mit Hilfe von ElasticSearch 15 Millionen Anfragen in 6 Minuten bearbeitet haben und die täglichen Lasten auf der Website eines unserer Kunden optimieren konnten. Leider müssen wir ohne Namen auskommen, da wir eine NDA haben; wir hoffen, dass der Inhalt des Artikels davon nicht leidet. Lass uns anfangen.
Wie das Projekt aufgebaut ist
In unserem Backend erstellen wir Dienste, die die Funktionsfähigkeit der Websites und der mobilen Anwendung unseres Kunden sicherstellen. Die Gesamtstruktur ist im Diagramm zu sehen:

Im Laufe der Arbeit verarbeiten wir eine große Anzahl von Transaktionen: Käufe, Auszahlungen, Benutzerkontenaktionen, für die wir viele Protokolle speichern, und außerdem importieren und exportieren wir diese Daten in externe Systeme.
Es gibt auch Rückprozesse, bei denen wir Daten vom Kunden erhalten und diese an die Benutzer weitergeben. Außerdem gibt es auch Prozesse für die Abwicklung von Zahlungen und Bonussystemen.
Eine kurze Hintergrundgeschichte
Ursprünglich haben wir PostgreSQL als einziges Datenspeicher verwendet. Seine Standardvorteile für Datenbanken: Transaktionen, ausgefeilte Abfragesprache, umfangreiche Integrationswerkzeuge; in Kombination mit guter Leistung haben sie unsere Bedürfnisse lange Zeit erfüllt.
Wir haben in Postgres absolut alle Daten gespeichert: von Transaktionen bis Nachrichten. Doch die Anzahl der Benutzer stieg, und damit auch die Anzahl der Anfragen.
Zur Veranschaulichung: Die jährliche Anzahl der Sitzungen im Jahr 2017 nur auf der Desktop-Website beträgt 131 Millionen. 2018 waren es 125 Millionen. 2019 wieder 130 Millionen. Wenn man noch 100-200 Millionen von der mobilen Version der Website und der mobilen Anwendung hinzufügt, ergibt das eine kolossale Anzahl von Anfragen.
Mit dem Wachstum des Projekts konnte Postgres die Last nicht mehr bewältigen, wir kamen nicht hinterher – es gab eine große Anzahl von unterschiedlichen Anfragen, für die wir nicht genügend Indizes erstellen konnten.
Wir wussten, dass es notwendig war, andere Datenspeicher in Betracht zu ziehen, die unsere Bedürfnisse abdecken und die Last von PostgreSQL nehmen würden. Elasticsearch und MongoDB wurden als mögliche Varianten in Betracht gezogen. Letzteres weist in den folgenden Punkten Nachteile auf:
- Langsame Indizierungsgeschwindigkeit mit zunehmendem Datenvolumen in den Indizes. Bei Elastic hängt die Geschwindigkeit nicht vom Datenvolumen ab.
- Keine Volltextsuche
So haben wir uns für Elastic entschieden und uns auf den Umstieg vorbereitet.
Umstieg auf Elastic
1. Wir haben den Umstieg mit dem Suchdienst für Verkaufsstellen begonnen. Unser Kunde hat insgesamt etwa 70.000 Verkaufsstellen, und es werden mehrere Suchtypen auf der Website und in der App benötigt:
- Textsuche nach dem Namen des Ortes
- Geo-Suche in einem bestimmten Umkreis von einem Punkt. Zum Beispiel, wenn der Nutzer sehen möchte, welche Verkaufsstellen am nächsten zu seinem Wohnort sind.
- Suche in einem bestimmten Quadrat – der Nutzer umrisse ein Quadrat auf der Karte, und ihm werden alle Punkte in diesem Umkreis angezeigt.
- Suche nach zusätzlichen Filtern. Verkaufsstellen unterscheiden sich hinsichtlich des Sortiments
Wenn wir über die Organisation sprechen, haben wir in Postgres die Datenquelle sowohl für die Karte als auch für Nachrichten, während in Elastic Snapshots der Originaldaten erstellt werden. Das Problem war, dass Postgres ursprünglich mit der Suche nach allen Kriterien überfordert war. Es gab viele Indizes, die sich auch überlappten, weshalb der Planer in Postgres verwirrt war und nicht wusste, welchen Index er verwenden sollte.
2. Als Nächstes kam die Nachrichtenrubrik. Auf der Website erscheinen täglich Publikationen, damit der Nutzer im Informationsfluss nicht verloren geht, müssen die Daten vor der Ausgabe sortiert werden. Dazu ist die Suche notwendig: Auf der Website kann man nach Textübereinstimmung suchen und gleichzeitig zusätzliche Filter aktivieren, da auch diese über Elastic eingerichtet sind.
3. Dann haben wir die Verarbeitung von Transaktionen verlagert. Nutzer können bestimmte Produkte auf der Website kaufen und an Gewinnspielen teilnehmen. Nach solchen Käufen verarbeiten wir eine große Menge an Daten, insbesondere an Wochenenden und Feiertagen. Zum Vergleich: An gewöhnlichen Tagen liegt die Anzahl der Käufe bei etwa 1,5-2 Millionen, während sie an Feiertagen bis zu 53 Millionen erreichen kann.
Dabei müssen die Daten in minimaler Zeit verarbeitet werden – Nutzer warten nicht gerne mehrere Tage auf Ergebnisse. Mit Postgres sind solche Fristen nicht zu erreichen – wir erhielten oft Sperrungen, und während wir alle Anfragen bearbeiteten, konnten die Nutzer nicht überprüfen, ob sie Preise gewonnen hatten oder nicht. Das ist für das Geschäft nicht sonderlich angenehm, weshalb wir die Verarbeitung nach Elasticsearch verlagert haben.
Pulsation
Aktuell sind die Updatesereignisse nach folgenden Bedingungen eingestellt:
- Verkaufsstellen. Sobald wir Daten aus einer externen Quelle erhalten, starten wir sofort das Update.
- Nachrichten. Sobald auf der Website eine Nachricht bearbeitet wird, wird sie automatisch an Elastic gesendet.
Hier sollte man nochmals die Vorteile von Elastic erwähnen. In Postgres muss man während der Abfrage warten, bis alle Datensätze korrekt verarbeitet sind. In Elastic kann man 10.000 Datensätze senden und sofort mit der Arbeit beginnen, ohne warten zu müssen, bis sich die Datensätze auf alle Shards verteilt haben. Natürlich kann ein Shard oder eine Replica die Daten nicht sofort sehen, aber bald werden sie für alle verfügbar sein.
Integrationsmethoden
Es gibt zwei Integrationsmöglichkeiten mit Elastic:
- Über den nativen Client über TCP. Der native Treiber stirbt allmählich aus: Er wird nicht mehr unterstützt, und seine Syntax ist sehr unhandlich. Daher verwenden wir ihn praktisch nicht und versuchen, ganz darauf zu verzichten.
- Über die HTTP-Schnittstelle, in der man sowohl JSON-Anfragen als auch die Syntax von Lucene verwenden kann. Letzteres ist die Text-Engine, die Elastic nutzt. In diesem Fall erhalten wir die Möglichkeit des Batch-Uploads über JSON-Anfragen über HTTP. Genau diese Option versuchen wir zu verwenden.
Dank der HTTP-Schnittstelle können wir Bibliotheken verwenden, die eine asynchrone Implementierung des HTTP-Clients bieten. Wir können die Vorteile von Batch-Uploads und der asynchronen API nutzen, was letztendlich eine hohe Leistung ermöglicht, die uns an Tagen großer Aktionen sehr geholfen hat (mehr dazu später).
Einige Zahlen zum Vergleich:
- Speicherung von Benutzern, die Preise in Postgres in 20 Streams ohne Gruppierung erhalten haben: 460.713 Datensätze in 42 Sekunden
- Elastic + reaktiver Client mit 10 Streams + Batch auf 1000 Elemente: 596.749 Datensätze in 11 Sekunden
- Elastic + reaktiver Client mit 10 Streams + Batch auf 1000 Elemente: 23.801.684 Datensätze in 4 Minuten
Jetzt haben wir einen Anfrage-Manager über HTTP geschrieben, der JSON erstellt, sowohl Batch/Non-Batch und diese über jeden HTTP-Client sendet, unabhängig von der Bibliothek. Man kann auch wählen, ob man synchron oder asynchron Anfragen senden möchte.
In einigen Integrationen verwenden wir immer noch den offiziellen Transport-Client, aber das ist nur eine Frage des nächsten Refactorings. Für die Verarbeitung wird jedoch ein eigener Client verwendet, der auf dem Spring WebClient basiert.

Große Aktion
Einmal im Jahr findet auf dem Projekt eine große Aktion für die Benutzer statt — das ist das Highload, denn zu dieser Zeit arbeiten wir mit Zehntausenden von Millionen von Benutzern gleichzeitig.
In der Regel treten Spitzenlasten an Feiertagen auf, aber diese Aktion ist ganz auf einem anderen Niveau. Im Jahr vor zwei Jahren haben wir am Tag der Aktion 27.580.890 Produkte verkauft. Die Daten wurden mehr als eine halbe Stunde lang verarbeitet, was bei den Benutzern Unannehmlichkeiten verursachte. Die Benutzer erhielten Preise für ihre Teilnahme, aber es wurde klar, dass der Prozess beschleunigt werden muss.
Anfang 2019 haben wir beschlossen, dass wir ElasticSearch benötigen. Ein ganzes Jahr haben wir die Verarbeitung der erhaltenen Daten in Elastic und deren Bereitstellung über die API der mobilen Anwendung und der Website organisiert. Im Ergebnis haben wir im nächsten Jahr während der Aktion 15.131.783 Datensätze in 6 Minuten verarbeitet.
Da wir sehr viele Personen haben, die Produkte kaufen und an der Verlosung teilnehmen möchten, ist dies eine vorübergehende Maßnahme. Derzeit senden wir aktuelle Informationen an Elastic, planen jedoch, Archivdaten aus den vergangenen Monaten in Postgres zu übertragen, um ein permanentes Speicher zu schaffen. So vermeiden wir eine Überlastung des Elastic-Indexes, der ebenfalls seine Grenzen hat.
Fazit/Ergebnisse
Im Moment haben wir alle Dienste, die wir wollten, auf Elastic übertragen und jetzt eine Pause eingelegt. Derzeit bauen wir über dem Hauptspeicher in Postgres einen Index in Elastic auf, der die Benutzerlast aufnimmt.
In Zukunft planen wir, die Dienste zu übertragen, wenn wir erkennen, dass die Datenanfragen zu vielfältig werden und über eine unbegrenzte Anzahl von Spalten ausgesucht werden. Das ist bereits eine Aufgabe, die nicht für Postgres geeignet ist.
Wenn wir einen Volltextsuche in der Funktionalität benötigen oder wenn wir viele verschiedene Suchkriterien haben, wissen wir bereits, dass wir das in Elastic übertragen müssen.
⌘⌘⌘
Danke, dass Sie gelesen haben. Wenn in Ihrem Unternehmen ebenfalls ElasticSearch verwendet wird und Sie eigene Implementierungscases haben, erzählen Sie uns darüber. Es wäre interessant zu erfahren, wie es bei anderen ist 🙂
Quelle: habr.com
