Batch Size, Partial Reindex und die Bulk API
Bei Katalogen mit mehreren hunderttausend SKUs wird der catalogsearch_fulltext-Indexer schnell zum Flaschenhals im Betrieb: volle Reindex-Laeufe dauern Stunden, der Cron-Job blockiert andere Jobs, und PHP-Worker geraten an ihre Speichergrenze. Dieser Artikel zeigt, wie Batch Size, Partial-Reindex-Strategien und die Elasticsearch Bulk API zusammenspielen, um die Indexer-Performance bei grossen Katalogen spuerbar zu verbessern.
Inhaltsverzeichnis
- 1. Wo Indexer-Performance bei grossen Katalogen zum Problem wird
- 2. Batch Size Tuning fuer catalogsearch_fulltext
- 3. Update on Save vs. Update by Schedule
- 4. Partial-Reindex-Strategien im Detail
- 5. Was unter der Haube passiert: die Elasticsearch Bulk API
- 6. Cluster-seitiges Tuning waehrend Bulk-Laeufen
- 7. Monitoring: woran man einen langsamen Indexer erkennt
- 8. Typische Fallstricke bei der Optimierung
- 9. Konfigurationsvergleich fuer unterschiedliche Katalogtypen
- 10. Zusammenfassung
- 11. FAQ
1. Wo Indexer-Performance bei grossen Katalogen zum Problem wird
Solange ein Magento-Shop wenige tausend Produkte fuehrt, faellt schlechte Indexer-Performance kaum auf. Ein voller Reindex laeuft in wenigen Minuten durch, der Cron-Job stoert niemanden, und der Standard-Batch-Size-Wert aus der Werkskonfiguration reicht aus. Bei Katalogen mit mehreren hunderttausend SKUs, wie sie in B2B-Marktplaetzen oder Ersatzteilkatalogen ueblich sind, kippt dieses Bild vollstaendig: Ein voller Reindex-Lauf des catalogsearch_fulltext-Indexers kann mehrere Stunden dauern, blockiert dabei Datenbankressourcen und konkurriert mit anderen Cron-Jobs um CPU und Netzwerkbandbreite.
Die Indexer-Performance haengt dabei nicht an einer einzelnen Stellschraube, sondern an einem Zusammenspiel aus PHP-seitiger Batch-Verarbeitung, Datenbankabfragen fuer Produktdaten und der Effizienz, mit der Dokumente an Elasticsearch uebertragen werden. Wer nur an einer Stelle optimiert, etwa die PHP-Memory-Limits erhoeht, ohne die Batch Size anzupassen, verschiebt den Flaschenhals lediglich, statt ihn zu beseitigen. Dieser Artikel geht systematisch durch alle relevanten Stellschrauben, von der Konfiguration bis zum laufenden Monitoring.
Ein praktischer Ausgangspunkt fuer jede Optimierung ist die Messung: Wie lange dauert ein voller Reindex aktuell, wie viele Produkte werden pro Sekunde verarbeitet, und an welcher Stelle im Prozess entsteht die groesste Wartezeit. Ohne diese Baseline laesst sich der Effekt jeder Aenderung an der Indexer-Performance nicht objektiv beurteilen.
2. Batch Size Tuning fuer catalogsearch_fulltext
Die zentrale Stellschraube fuer die Indexer-Performance ist die Batch Size, konfiguriert in app/etc/env.php unter dem Schluessel indexer.batch_size. Magento verarbeitet Produkte fuer den Fulltext-Index nicht einzeln, sondern in Batches, die aus der Datenbank geladen, angereichert und anschliessend gebuendelt an Elasticsearch uebertragen werden. Eine zu kleine Batch Size erzeugt viele kleine Datenbankabfragen und viele kleine Bulk-Requests, was den Overhead durch Netzwerklatenz und Verbindungsaufbau in die Hoehe treibt. Eine zu grosse Batch Size dagegen laesst PHP-Prozesse an ihr Memory-Limit stossen und kann Elasticsearch mit zu grossen Bulk-Payloads ueberfordern.
Der Default-Wert fuer catalogsearch_fulltext liegt bei 100 Dokumenten pro Batch, was fuer kleine bis mittlere Kataloge angemessen ist, bei sehr grossen Katalogen aber oft zu konservativ. In der Praxis zeigen Werte zwischen 300 und 1000 haeufig bessere Durchsatzwerte, abhaengig von der durchschnittlichen Dokumentgroesse. Produkte mit vielen Attributen, langen Beschreibungen und zahlreichen Bildern erzeugen deutlich groessere Elasticsearch-Dokumente als einfache SKUs, weshalb die optimale Batch Size stark vom Produktdatenmodell abhaengt und nicht pauschal uebernommen werden sollte.
// app/etc/env.php - indexer batch size configuration
'indexer' => [
'batch_size' => [
'catalogsearch_fulltext' => [
'partial' => 500,
'full' => 500,
],
'catalog_product_price' => [
'partial' => 500,
'full' => 500,
],
],
],
# After changing env.php, run a full reindex once to validate memory usage
bin/magento indexer:reindex catalogsearch_fulltext
3. Update on Save vs. Update by Schedule
Neben der Batch Size beeinflusst der Indexer-Modus die wahrgenommene Indexer-Performance massiv. Im Modus "Update on Save" laeuft der Reindex synchron bei jeder Produktaenderung, was bei einzelnen Aenderungen unproblematisch ist, bei Bulk-Importen aber katastrophal wirkt: Ein CSV-Import mit zehntausend Produkten wuerde zehntausend einzelne, synchrone Reindex-Trigger ausloesen. Der Modus "Update by Schedule" (mview) entkoppelt Aenderung und Reindex: Produktaenderungen markieren betroffene Datensaetze in einer Changelog-Tabelle, und ein Cron-Job verarbeitet diese in kontrollierten Batches im Hintergrund.
Fuer Kataloge mit haeufigen Bulk-Updates ist "Update by Schedule" nahezu immer die richtige Wahl. Die relevante Cron-Group heisst indexer, und die Frequenz des zugehoerigen Jobs indexer_update_all_views laesst sich ueber die Crontab-Konfiguration anpassen. Wichtig ist, den Cron-Job nicht zu selten laufen zu lassen, weil sich sonst das Changelog aufstaut und ein einzelner Lauf ploetzlich zehntausende Datensaetze verarbeiten muss, was wiederum die Indexer-Performance in diesem einen Lauf drastisch verschlechtert.
# Switch catalogsearch_fulltext to scheduled (mview) mode
bin/magento indexer:set-mode schedule catalogsearch_fulltext
# Verify current mode for all indexers
bin/magento indexer:show-mode
# Example output
# Title Mode
# Catalog Search Schedule
# Product Price Schedule
# Product EAV Update on Save
4. Partial-Reindex-Strategien im Detail
Ein voller Reindex verarbeitet jedes Produkt im Katalog neu, unabhaengig davon, ob es sich geaendert hat. Bei einem Katalog mit 500.000 SKUs, von denen taeglich nur wenige tausend aktualisiert werden, ist das enorm ineffizient. Der Partial Reindex im Schedule-Modus loest genau dieses Problem: ueber das Mview-System (Materialized View) werden nur die Produkte neu indexiert, deren zugrunde liegende Daten sich seit dem letzten Lauf geaendert haben. Die zentrale Tabelle dafuer ist catalogsearch_fulltext_cl (Change Log), in der jede relevante Aenderung als Zeile mit der betroffenen Entity ID landet.
Fuer eine hohe Indexer-Performance bei Partial Reindexes ist entscheidend, welche Aenderungen ueberhaupt einen Eintrag im Changelog erzeugen. Preisaenderungen, Lagerbestandsaenderungen und Attributaenderungen, die als "used in search" markiert sind, loesen einen Eintrag aus. Attribute, die nicht suchbar oder nicht filterbar konfiguriert sind, erzeugen dagegen keinen Reindex-Trigger, was ungenutzte Attribut-Flags zu einem einfachen, aber oft uebersehenen Hebel fuer bessere Indexer-Performance macht: Wer Attribute konsequent nur dort als suchbar markiert, wo es tatsaechlich gebraucht wird, reduziert die Zahl der Changelog-Eintraege spuerbar.
| Katalogtyp | Empfohlene Batch Size | Indexer-Modus | Begruendung |
|---|---|---|---|
| Bis 10.000 SKUs | 100 (Default) | Update on Save | Voller Reindex laeuft in Sekunden, Sofortwirkung gewuenscht |
| 10.000 bis 100.000 SKUs | 300 | Update by Schedule | Balance zwischen Durchsatz und Speicherverbrauch |
| 100.000 bis 500.000 SKUs | 500 bis 750 | Update by Schedule | Weniger Bulk-Requests, mehr Durchsatz pro Aufruf |
| Ueber 500.000 SKUs | 1000, plus Parallelisierung | Update by Schedule, mehrere Cron-Consumer | Einzelner Prozess reicht nicht mehr fuer akzeptable Laufzeiten |
5. Was unter der Haube passiert: die Elasticsearch Bulk API
Egal ob voller oder partieller Reindex: Magento uebertraegt Dokumente nie einzeln an Elasticsearch, sondern immer ueber die _bulk-API. Jede Batch aus dem PHP-Indexer wird zu einem einzigen HTTP-Request mit mehreren Zeilen im NDJSON-Format zusammengefasst, wobei jede Aktion (typischerweise index) direkt gefolgt von den Dokumentdaten steht. Diese Buendelung ist der Hauptgrund, warum die Batch Size ueberhaupt einen so grossen Effekt auf die Indexer-Performance hat: Sie bestimmt direkt die Groesse jedes einzelnen Bulk-Requests.
Elasticsearch verarbeitet eingehende Bulk-Requests ueber einen dedizierten Thread Pool mit begrenzter Queue-Groesse. Werden zu viele Bulk-Requests gleichzeitig gesendet, etwa weil mehrere Cron-Consumer parallel laufen, kann die Queue volllaufen, und Elasticsearch beginnt, Requests mit TOO_MANY_REQUESTS (HTTP 429) abzulehnen. Magento behandelt diesen Fall nicht automatisch mit Retry-Logik in jeder Version, weshalb es bei zu aggressiver Parallelisierung zu fehlgeschlagenen, unvollstaendigen Indexlaeufen kommen kann, die im Log oft schwer von echten Datenfehlern zu unterscheiden sind.
POST /_bulk
{ "index": { "_index": "magento2_default_catalogsearch_fulltext_1", "_id": "10231" } }
{ "sku": "SHOE-BLK-42", "name": "Running Shoe Black", "price": 89.90, "visibility": 4 }
{ "index": { "_index": "magento2_default_catalogsearch_fulltext_1", "_id": "10232" } }
{ "sku": "SHOE-BLK-43", "name": "Running Shoe Black", "price": 89.90, "visibility": 4 }
// Response contains per-item status; a single failed item does not
// abort the whole batch, so failures must be checked individually
{
"took": 42,
"errors": false,
"items": [
{ "index": { "_id": "10231", "status": 201 } },
{ "index": { "_id": "10232", "status": 201 } }
]
}
6. Cluster-seitiges Tuning waehrend Bulk-Laeufen
Auf der Elasticsearch-Seite gibt es zwei Hebel, die waehrend grosser Reindex-Laeufe die Indexer-Performance deutlich verbessern. Der erste ist das temporaere Erhoehen des refresh_interval. Standardmaessig aktualisiert Elasticsearch den Suchindex jede Sekunde, was bei einer hohen Rate eingehender Bulk-Requests erheblichen internen Overhead erzeugt, weil jeder Refresh ein neues Lucene-Segment erstellt. Waehrend eines vollstaendigen Reindex-Laufs kann das Intervall auf 30 Sekunden oder sogar -1 (deaktiviert) gesetzt und danach wieder auf den Standardwert zurueckgesetzt werden.
Der zweite Hebel ist die temporaere Reduktion der Replica-Anzahl auf 0 waehrend eines initialen Bulk-Loads, etwa bei einer kompletten Neubefuellung eines Index nach einer Migration. Ohne Replicas muss Elasticsearch jedes Dokument nicht zusaetzlich auf andere Knoten replizieren, was den Schreibdurchsatz signifikant erhoeht. Nach Abschluss des Bulk-Loads wird die Replica-Anzahl wieder auf den produktiven Wert gesetzt, woraufhin Elasticsearch die Replicas im Hintergrund befuellt, ohne den laufenden Betrieb zu blockieren.
# Temporarily relax refresh interval and disable replicas during a bulk load
curl -X PUT "localhost:9200/magento2_default_catalogsearch_fulltext_1/_settings" \
-H "Content-Type: application/json" -d '{
"index": {
"refresh_interval": "30s",
"number_of_replicas": 0
}
}'
# Restore production settings after the bulk load finishes
curl -X PUT "localhost:9200/magento2_default_catalogsearch_fulltext_1/_settings" \
-H "Content-Type: application/json" -d '{
"index": {
"refresh_interval": "1s",
"number_of_replicas": 1
}
}'
7. Monitoring: woran man einen langsamen Indexer erkennt
Ohne Monitoring bleibt jede Aussage zur Indexer-Performance spekulativ. Der einfachste Einstiegspunkt ist die Tabelle cron_schedule, in der sich die tatsaechliche Laufzeit des indexer_update_all_views-Jobs ueber die Zeit nachvollziehen laesst. Ein stetig wachsender Trend bei der Laufzeit ist ein fruehes Warnsignal dafuer, dass der Katalog schneller waechst als die aktuelle Konfiguration verkraftet. Ergaenzend liefert bin/magento indexer:status den aktuellen Status, aber keine historischen Laufzeitdaten, weshalb ein externes Monitoring-System fuer belastbare Trendaussagen noetig ist.
Auf Elasticsearch-Seite lohnt sich ein Blick auf die Thread-Pool-Statistiken fuer write beziehungsweise bulk, ueber die _nodes/stats/thread_pool-API. Eine hohe Zahl an rejected-Eintraegen zeigt direkt, dass Bulk-Requests abgelehnt wurden, oft weil zu viele Anfragen gleichzeitig eintrafen. Diese Metrik ist zuverlaessiger als reine CPU-Auslastung, weil sie das tatsaechliche Symptom schlechter Indexer-Performance misst, statt nur eine Ressourcenauslastung, die auch aus anderen Gruenden hoch sein kann.
# Track catalogsearch_fulltext cron runtime over time
bin/mysql magento -e "
SELECT job_code, scheduled_at, executed_at, finished_at,
TIMESTAMPDIFF(SECOND, executed_at, finished_at) AS duration_seconds
FROM cron_schedule
WHERE job_code = 'indexer_update_all_views'
AND status = 'success'
ORDER BY scheduled_at DESC
LIMIT 20;
"
# Check Elasticsearch bulk thread pool for rejections
curl -s "localhost:9200/_nodes/stats/thread_pool/write,bulk?pretty" \
| grep -A 3 '"rejected"'
8. Typische Fallstricke bei der Optimierung
Der haeufigste Fehler ist, die Batch Size drastisch zu erhoehen, ohne die PHP-Memory-Limits entsprechend anzupassen. Ein PHP-Prozess, der Produktdaten fuer 1000 Dokumente gleichzeitig im Speicher haelt, braucht deutlich mehr RAM als einer mit 100 Dokumenten, insbesondere bei Produkten mit vielen Attributen. Das Ergebnis ist ein Fatal Error wegen erschoepftem Memory Limit mitten im Reindex-Lauf, der den gesamten Batch ungueltig macht und den Prozess von vorn beginnen laesst.
Ein zweiter Fallstrick ist paralleles Ausfuehren mehrerer indexer:reindex-Aufrufe ohne Koordination, in der Annahme, das beschleunige den Prozess proportional. Tatsaechlich konkurrieren parallele Prozesse um dieselben Datenbankverbindungen und denselben Elasticsearch-Bulk-Thread-Pool, was ab einer gewissen Parallelitaet die Gesamtdurchsatzrate wieder senkt statt sie zu erhoehen. Ein dritter, seltener beachteter Fallstrick betrifft die MySQL-Seite: Fehlende oder veraltete Indizes auf den vom Indexer genutzten Flat-Tabellen koennen die Datenbeschaffung fuer jede Batch verlangsamen, lange bevor Elasticsearch ueberhaupt beteiligt ist.
9. Konfigurationsvergleich fuer unterschiedliche Katalogtypen
Die konkrete Konfiguration fuer optimale Indexer-Performance unterscheidet sich stark je nach Katalogtyp. Ein B2B-Ersatzteilkatalog mit sehr vielen, aber datenarmen SKUs verhaelt sich anders als ein Fashion-Katalog mit wenigen tausend Produkten, aber umfangreichen Beschreibungen, Bildern und Variantendaten. Die Tabelle aus Abschnitt 4 liefert dafuer grobe Richtwerte, aber die tatsaechlich optimale Batch Size sollte immer empirisch ermittelt werden, indem mehrere Werte unter realistischer Last getestet und die jeweilige Laufzeit gemessen wird.
Ein bewaehrtes Vorgehen ist, mit dem Default-Wert zu starten, die Batch Size schrittweise in 100er-Schritten zu erhoehen und nach jedem Schritt die Laufzeit sowie den Speicherverbrauch der PHP-Worker zu protokollieren. Der Punkt, an dem sich die Laufzeitverbesserung deutlich abflacht oder Memory-Fehler auftreten, markiert die praktische Obergrenze fuer die Indexer-Performance-Optimierung ueber die Batch Size allein. Ab diesem Punkt bringt nur noch Parallelisierung oder zusaetzliche Hardware weitere Verbesserungen.
Mironsoft
Elasticsearch- und Indexer-Performance-Tuning fuer grosse Magento-Kataloge
Reindex-Laeufe, die Stunden statt Minuten dauern?
Wir analysieren Batch Size, Indexer-Modus und Elasticsearch-Cluster-Einstellungen und bringen die Indexer-Performance eures Katalogs auf ein produktionstaugliches Niveau.
Performance-Audit
Baseline-Messung von Reindex-Laufzeiten und Bulk-Durchsatz
Batch-Size-Tuning
Empirische Ermittlung der optimalen Batch Size fuer euren Katalog
Cluster-Tuning
Refresh Interval, Replicas und Thread-Pool-Konfiguration fuer Bulk-Laeufe
10. Zusammenfassung
Gute Indexer-Performance bei grossen Magento-Katalogen entsteht nicht durch eine einzelne Einstellung, sondern durch das Zusammenspiel aus passender Batch Size, dem richtigen Indexer-Modus, effizienter Partial-Reindex-Nutzung ueber das Mview-System und einer Elasticsearch-Konfiguration, die waehrend Bulk-Laeufen temporaer angepasst wird. Der Modus "Update by Schedule" ist fuer Kataloge ab mittlerer Groesse fast immer Pflicht, weil er Bulk-Aenderungen entkoppelt statt sie synchron und einzeln zu verarbeiten.
Die richtige Batch Size laesst sich nicht pauschal empfehlen, sondern muss empirisch fuer den jeweiligen Katalog ermittelt werden, abhaengig von Dokumentgroesse und verfuegbarem PHP-Memory. Monitoring ueber Cron-Laufzeiten und Elasticsearch-Thread-Pool-Statistiken macht Verschlechterungen der Indexer-Performance sichtbar, bevor sie zu einem akuten Betriebsproblem werden. Wer diese Bausteine konsequent umsetzt, haelt Reindex-Laufzeiten auch bei wachsendem Katalog in einem planbaren Rahmen.
Indexer-Performance bei grossen Katalogen, das Wichtigste auf einen Blick
Batch Size
In env.php unter indexer.batch_size, empirisch zwischen 300 und 1000 fuer grosse Kataloge ermitteln.
Indexer-Modus
"Update by Schedule" entkoppelt Aenderung und Reindex und verhindert synchrone Massentrigger bei Bulk-Importen.
Bulk API
Jede Batch wird als ein einziger _bulk-Request uebertragen, dessen Groesse direkt von der Batch Size abhaengt.
Cluster-Tuning
refresh_interval hochsetzen und Replicas temporaer auf 0 setzen, waehrend eines grossen Bulk-Loads.