Performance optimierung Indexierung

Hallo,

wir haben starke performance Probleme in Sachen Indexierung und eventuell hat jemand einen Rat.

Unser Setup:

Hauptserver:
Hetzner cloud Vserver 32vCPU (dedizierte CPU-Ressourcen), 128GB RAM

  • Shopware 6.3.4.1
  • NGINX 1.14.1
  • PHP-FPM 7.4.14
  • MariaDB 10.3.17

Elasticsearch:
Hetzner cloud Vserver 16vCPU (dedizierte CPU-Ressourcen), 64GB RAM

  • NGINX 1.14.1
  • Elasticsearch 7.10.2

Produktanzahl im Shop: ~2Mio

Zur Frage:

Was super läuft ist der normale Betrieb, das Suchen im frontend etc geht dank elasticsearch super flott.
Importieren der Artikel über die Sync API ist auch super flott - alles spitze bisher bei mehreren Mio Artikeln.

Probleme bereitet das indexieren Shopwareseitig. Das dauert ewig (pro Bulk!) egal ob über die CLI direkt oder ausgelagert als queue.

Variante 1: Indexing über die CLI:

bin/console dal:refresh:index

Das rennt ein paar Stunden, was kein thema ist - allerdings füllt sich der RAM stück für stück und ist irgendwann dicht:

Memory limit ist hier testweise deaktiviert um zu sehen wie weit der Spaß geht: bis er voll ist. Wir starten bei ca 20Gb Auslastung des Speichers und nach Stunden sind wir beim Limit von 128GB angekommen durch das Indexing command.

Bisher ist das durch unserem RAM gedeckelt worden aber bei noch mehr Produkten wirds eng.

Was kann sein, das das Command so viel RAM verbraucht bzw den RAM immer weiter füllt?

Variante 2 queue indexing:

Wenn die Artikel über die Sync API mit queue indexing importiert wurden klappt das so weit und wir haben zig tausende messages im table die abgearbeitet werden:

bin/console messenger:consume

Wenn ich das mal per Hand mitverfolge (-vv option) dann rattert er minuten für einen Bulk Shopwareseitig und zum Schluss das Indexieren in der Elastic und refreshen des Indexex geht in sekunden.

MariaDB wurde auf dem hauptserver auf dem auch Shopware läuft (siehe Setup oben) genug Speicher gegeben (ca. 80GB) wie hier beschrieben:

[mysqld]
key_buffer_size = 10M
innodb_buffer_pool_size = 80000M

und FPM hat 512mb für Shopware bereitgestellt.

Was könnte ich am Shopwareseitigen indexierungs-part noch optimieren?

Eventuell hat ja jemand einen Tip. Mehrere Mio produkte rattern so Wochen oder Monate.

Danke im Vorraus!

Keiner eine Idee oder das selbe Problem mit der Performance beim Indexierung von mehreren millionen Produkten?

Hast du das ganze schon versucht mittels supervisord? 

Somit solltest du festlegen können wieviele Prozesse gleichzeitig gestartet werden sollen, welche die message queue schneller verarbeiten.

Und ggfls. mit einem time limit oder memory limit versehen sollte auch bei messenger:consume funktionieren . Shopware 6: Scheduled Tasks

 

Hallo,

das messenger command läuft mehrfach mit supervisor, es geht wie gesagt tatsächlich um die einzelne länge eines bulks. Wenn ich einen einzelnen messenger mit -vv starte sehe ich das Shopwareseitig ewig gearbeitet wird pro bulk - der elastic-part passt zum Schluss.

Ich weiß nicht genau was beim Shopware-seitigem Indexieren alles gemacht wird aber sicher SEO URL’s e.t.c.

Ich befürchte ich muss das genauer untersuchen.

Beispiel:

  1. Importiere ich per Sync API einen Bulk von 100 Produkten geht das mit Header ‘queue-indexing’ flott, klar er importiert nur und indexiert später.

  2. Mach ich den gleichen Sync-API call ohne indexing Header (also sofortige indexierung) dauern 100 Produkte in einem bulk ca 45-60 sekunden.

Wir reindexieren auch gerade, und sind aktuell bei 50GB RAM auf dem Server und „nur“ 80.000 Produkten. Also den Server-RAM von 30 auf 40 auf 50 hochskaliert, aber am Ende immer Speicherüberlauf, und kein Ende in Sicht.
Hast du das schon per Queue probiert?
bin/console dal:refresh:index --use-queue
danach regelmäßig:
bin/console messenger:consume --time-limit=900 -v
oder:
bin/console messenger:consume --memory-limit=1G

Hallo Ansgar,

wie erwähnt lief der refresh irgendwann durch, bei 128GB RAM aber auch knapp vor voll. Problem sind eher die nachfolgenden IMPORTS die per Sync API als queue indexing gemacht wurden. Die messages werden erfolgreich geschrieben und wir haben 40! worker parallel laufen. Die schaffen leider „nur“ ca ~ 75.000 Produkte pro 24h. Wir sind jetzt bei nicht ganz 6MIO und haben noch ca ~4MIO vor uns.

Ich weiß nicht was so ewig rattert aber wenn ich die messages verfolge wird eine message angefangen (40 genauer gesagt aber hier mal Beispiel von einer) dann passiert irgendwas (DB seitig sicher) was 10 - 15min dauert und dann sehe ich die command sendings zur elasticsearch die relativ flott gehen, dann fängt eine neue message an die wieder 10-15min irgendwas Db seitig macht und dann wieder der schnelle husch in die elasticsearch)

Wir haben uns jetzt damit abgefunden aber wenn wir die 10Mio mal geschafft haben, sind 133 Tage vergangen. Mir grault es jemals neu indexieren zu müssen und der Kunde ist auch nicht amüsiert über diese Performance. Er hat mal über eine Enterprise Version nachgedacht aber ich glaube er ist sehr genervt von dem ganzen Ding und mag Shopware jetzt nicht mehr so :slight_smile: …Kein plan ich fasse solche Produktmengen nie wieder mit Showpare an glaube ich auch wenn ich es als Symfony dev sehr schätze und alles eigentlich in die richtige Richtung geht! Ich kriege an keiner Stelle Infos dazu, weder Slack noch hier. Ich weiß es gibt ein Ticket und es steht auf der Roadmap aber ich werde den Teufel tun das Indexieren jemals wieder anzustoßen :). Für kleinere Shops ziehe ich Shopware sicher wieder in betracht.

Also ich kenne mich nicht so ganz damit aus, aber habe kürzlich ein Import Tool für 30.000 Produkte geschrieben → Auf dem Dev-Server schoss die Auslastung bis ins unermessliche hoch (bei 4gb war Schluss). Habe dann den Import in mehrere kleine Parts gesplittet… Der Import hat ca. 45-60min gedauert wobei ich wirklich High-End Hardware zum lokalen Entwickeln besitze…

Aber auf dem Live-Server (Standard Webhosting Managed Server) war der Max-Peak bei schlappen ~300MB, der Import hat auch nur 20min gedauert.

Könnte evtl hier dran liegen?! (SQL-Logger)

Hallo Moorleiche,

30.000 Produkte in 20min ist natürlich super. Wie gesagt der Import selber lief flux durch, allerdings mit queue indexing - er schreibt dann zwar alles schnell, indexiert aber per messages. Hier ist dann das Problem, zig tausende messages die jetzt eben langsam rattern (~75.000 Produkte pro tag). System ist produktiv, also der Logger ist es nicht. Wie gesagt, ich lasse das jetzt rattern und gut ist.

Schade, aber rein von der Logik her dürfte der Speicher gar nicht voll laufen… Und die Produkte sollten eigentlich direkt indexiert werden - ja sofern man auch die Produkte mit dem EntityManager anlegt. Aber selbst die Indexierung über den Admin Worker oder die Console dürfte keinen Speicher Fressen - ist wahrscheinlich ein Fehler im SW Core, sodass der Speicher nach Indexierung eines Produktes nicht wieder freigegeben wird. Vielleicht landen die SEO Urls ja im Zwischenspeicher, damit man sich eine Datenbankabfrage zum prüfen - ob schon vorhanden - spart. Aber selbst das sollte nicht soviel Speicher fressen. Ich gehe wirklich davon aus, dass das ein Problem mit den EntityManager ist. In den Symfony Foren wird auch darüber berichtet. Da gibt es auch einen weiteren Lösungsansatz:

$this->connection->getWrappedConnection()->setAttribute(\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);

Ja es gibt auch ein ticket dazu und es stehen Verbesserungen auf der Roadmap zum Thema indexieren.
Ich sollte vielleicht erwähnen das ich nicht direkt mit den Repositories arbeiten kann da wir über die API von extern importieren. Das macht jetzte rstmal kein unterschied außer das ich halt nicht in der shopware runtime zum Zeitpunkt des Importierens stecke.

Ja ich denke auch es wird irgendwas an Speicher nicht wieder freigegeben und staut sich auf, mal abwarten was die Änderungen bringen die auf der Roadmap stehen (die sind bei 93% bezüglich Elastic) aber ich denke ich werde nix neu anstoßen :slight_smile:

Eine Optimierung für den Elastic ist die Anzahl Sprachen in SW schlank zu halten - pro Sprache wird ein Index angelegt. Und wie ich gerade bemerke hatten wir abgesehen von Deutsch und Englisch aus einer Testphase noch 2 weitere Sprachen angelegt, die ich gleich löschen werde. Sowas wie de-AT unterscheidet sich in der Regel nicht von de-DE. Ob das den Reindex beschleunigt weiß ich aber noch nicht.

Wir arbeiten nur mit einer Sprache, haben aber einige SalesChannel mit jeweils http und https Domain im SalesChannel. Es werden SEO URLS erstellt pro SalesChannel auch wenn die Produkte nicht dem SalesChannel hinzugefügt sind. Das ist glaube ich eines der Hauptprobleme. Es werden für unsere 10Mio Produkte pro SalesChannel SEO URLs generiert. In den SalesChannels haben wir nur ein paar ausgewählte Produkte (max 50!) und somit ist das Indexieren (bzw Erstellen von SEO URLs) von allen 10Mio Produkten für diese SalesChannel eigentlich nicht nötig.

Das kann ich bestätigen. Dabei wäre über die visibility (Produkt > Zuweisung > Sichtbarkeit) eigentlich klar welches Produkt in welchem Kanal benötigt wird.

Bei uns sind es keine 10Mio aber immerhin 80Tsd, ein Reindex dauert auf unserem Server (pro Sprache) 2h.

Der Import über die Admin-API geht über den EntityManager und somit auch den EntityIndexer - aber man kann den wahrscheinlich abschalten?!

Mich würde nun mal nur interessieren wieso der Speicher so voll läuft. Auf dem Ersten Blick finde ich keinen Anhaltspunkt dass etwas geloggt wird (außer im dev Modus). Nur mal so zum Vergleich: im dev Modus geht der Speicher ca 20 mal schneller voll als im prod.

Aber ich erinnere mich, dass ich auch noch vor kurzem eine Indexierung via ES auf einer anderen Maschine durchführen wollte, war auch extrem langsam und Speicherlastig. Langsam lässt sich noch erklären, aber auch hier sollte der Speicher nicht voll laufen.

Der Import über die Admin-API geht über den EntityManager und somit auch den EntityIndexer - aber man kann den wahrscheinlich abschalten?!

Das Indexing kann man beim Sync-API request setzen: indexing-behavior ist hier der header key mit folgenden values.

Das sorgt für einen flotten Import, verlagert aber das Indexing Problem eben nur in die Message queue.

Egal, irgendwann wird es schon enden :slight_smile:

Das indexieren dauert bei uns auch sehr sehr lange, allerdings nur bei Shops mit >30k Artikeln und mit vielen Varianten. Ich glaube das hier die Krux in den Varianten und den vielen Update befehlen des ProductIndexer liegt.

Hier muss entweder ein abgespeckter Indexer her oder dem Payload können parameter mitgegeben werden was genau indexiert werden muss. Denn nicht immer braucht man ein ListingpriceUpdate oder ein stockUpdate etc.

//        $this->stockUpdater->update($ids, $context);
//
//        $this->variantListingUpdater->update($parentIds, $context);
//
//        $this->childCountUpdater->update(ProductDefinition::ENTITY_NAME, $parentIds, $context);
//
//        $this->manyToManyIdFieldUpdater->update(ProductDefinition::ENTITY_NAME, $ids, $context);
//
//        $this->categoryDenormalizer->update($ids, $context);
//
//        $this->listingPriceUpdater->update($parentIds, $context);
//
//        $this->ratingAverageUpdater->update($parentIds, $context);
//
//        $this->searchKeywordUpdater->update($ids, $context);

Aktuell kann ich keinem SW6 empfehlen bei > 25k Produkten und am besten noch ein begrenztes Budget was die Serverinfrastruktur angeht (obwohl die wie oben ersichtlich auch nicht hilft - ok 2Mio Produkte sind auch eher ein „Edgecase“)

Ich schreibe gerade einen Blogartikel darüber und bin bereits über folgende Problematik gerade bei Variantenartikeln gestoßen.

Ändert man einen Artikel oder Variante wir die gleiche Index Nachricht X (Variantenanzahl) in die Queue geschrieben. Sprich hat man eine Variante (bei uns Wobbler zum fischen → viele Farben und Größen) mit 120 Varianten, wird diese wieder und wieder indexiert, mit meistens keinen wirklich neuen Werten, da man meistens das ganze in einem Payload übergibt.

Ich gehe davon aus, dass dein +1 Mio Produkte Shop viele Varianten hat. Die Optimierung hierfür wäre den Payload so aufzusplitten, dass du den ersten Call mit „disable-indexing“ machst (in dem alle Varianten sind) und den zweiten einen Call mit „use-queue-indexing“ machst in dem der Hauptartikel ist (von dem ja die anderen dann erben.

Detaillierten Version davon gibt es in kürze hier


Noch besser fände ich aber eine SW Lösung welche über einen Key o.Ä. prüft ob die Message schon vorhanden ist und diese nicht in die Queue packt.

PS: NEXT-15739 - Added skip option to dal refresh index command · shopware/platform@bf3b5f5 · GitHub
hier kann man jetzt schon mal skippen :slight_smile: