Magentos SOAP-Schnittstelle ist Segen und Fluch zugleich: einerseits ermöglicht sie externen Systemen einen einfachen Zugriff auf den Webshop, andererseits sorgt sie bei Entwicklern aufgrund fehlender Funktionalitäten und vor allem der verbesserungsfähigen Performance immer wieder für munteres Hirnwinden. Magento hat neulich angekündigt, in beiderlei Hinsicht nachzulegen (mehr dazu im Ausblick auf Magento 1.5), doch bis dahin muss man mit der aktuellen API auskommen.
Mit Magento 1.4 bietet das Backend die Möglichkeit, das automatische Index-Management zu deaktivieren. Da ich mir immer schon dachte, dass sich die ständige Neuindizierung nicht positiv auf die Geschwindigkeit des Produktimports auswirken kann, war ich gespannt, welche Unterschiede sich durch die neue Option ergeben. Dieses Wochenende habe ich mir schließlich Zeit genommen, um ein paar grundlegende Tests durchzuführen.
Wer auf das Drumherum verzichten will, kann direkt zum Fazit springen. ![]()
Ziel
Ich wollte die Frage klären:
Wie wirken sich die Aktivierung bzw. Deaktivierung des Caches und des automatischen Index-Managements auf die Geschwindigkeit des Produkt-Imports via SOAP-API aus?
Je nach Hardware-Setup, Konfiguration und Produktdaten sind die Unterschiede natürlich gravierend, weswegen sich aus den Ergebnissen nur Tendenzen ablesen lassen werden.
Setup
Als Hardware musste der folgende Rechner herhalten:
| Komponente | Spezifikation |
|---|---|
| CPU | Intel Core2 Q6600 @ 2.40Ghz |
| RAM | 4GB DDR2-800 |
| HDD | Western Digital Caviar Blue, SATA II at 7200rpm, Betrieb im RAID1-Verbund |
Das Wirtsystem ist Windows XP Professional SP3. Auf diesem laufen Antivirus-Programm und Software-Firewall (ich will mir doch nichts einfangen
). Darauf läuft mittels Oracle VM VirtualBox 3.2.10 folgendes Gastsystem:
| Komponente | Version |
|---|---|
| Betriebssystem | Debian 5.0.6 ("Lenny") virtualisiert mit Oracle VM VirtualBox 3.2.10 |
| Webserver | Apache 2.2.9 |
| DBMS (Datenbank) | MySQL 5.0.51a-24 |
| PHP | PHP 5.2.6-1 |
Dem Gastsystem werden 4 Prozessoren und 1,5 GB RAM zugestanden. Es handelt sich um eine Debian-Standardinstallation mit den aktuellen Stable-Packages. Die Konfiguration von Webserver, Datenbank etc. wurde nicht verändert.
Shop & Daten
Getestet wird Magento 1.4.1.1 in der Version Community Edition. Für den Cache wird das Dateisystem verwendet.
Pro Test werden über die SOAP-API v1 1.000 simple Produkte mit dem Standard-Attributset angelegt. Die Produkte werden zufällig einer von fünf Kategorien zugeordnet. Bilder werden nicht importiert.
Die Produktdaten sind so simpel wie möglich gehalten, damit das Produkt gültig angelegt wird:
| Produktdaten | Werte |
|---|---|
| Kategorie | Zuordnung zu einer von fünf Kategorien |
| Website | Zuordnung zur Main-Website |
| Name | "Produktname" (statischer Text) |
| Kurzbeschreibung | "Kurzbeschreibung" (statischer Text) |
| Beschreibung | "Beschreibung" (Statischer Text9 |
| Preis | 9.99 |
| Gewicht | 0 |
| Lagerverwaltung | Array mit den folgenden Werten: qty: 1 is_in_stock: 1 use_config_manage_stock: 1 |
| Status | 1 (aktiviert) |
| Steuerklasse | Zufällige Zuordnung zu einer der vorhandenen Steuerklassen |
| Sichtbarkeit | Katalog, Suche |
Ablauf
Vier Konfigurationen werden getestet:
- Test 01:
- Cache-Management: Disabled
- Index-Management: Update on Save
- Test 02:
- Cache-Management: Disabled
- Index-Management: Manual Update
- Test 03:
- Cache-Management: Enabled
- Index-Management: Update on Save
- Test 04:
- Cache-Management: Enabled
- Index-Management: Manual Update
Wie erwähnt werden pro Test 1.000 Produkte über die SOAP-API in Version 1 erzeugt. Die benötigte Zeit wird für jedes Produkt einzeln protokolliert. Nach jedem Testlauf werden die Ergebnisse exportiert und der Sicherungspunkt des Images vor dem Testlauf wiederhergestellt. Damit hat jeder Lauf dieselben Ausgangsbedingungen.
Die Testreihe wurde 2-mal durchgeführt (an unterschiedlichen Tagen, mit Neustart) und die Ergebnisse zusammengeführt. Damit sollten Seiteneffekte, die durch das Wirtsystem entstehen, vermindert werden.
Ergebnisse
| Test | Durchschnitt | Standardabweichung | Minimum | 1. Quartil | Median | 3. Quartil | Maximum |
|---|---|---|---|---|---|---|---|
| Test 01 | 1,133 | 0,083 | 0,932 | 1,108 | 1,137 | 1,156 | 2,117 |
| Test 02 | 0,858 | 0,041 | 0,730 | 0,845 | 0,862 | 0,881 | 1,094 |
| Test 03 | 0,773 | 0,038 | 0,692 | 0,752 | 0,767 | 0,787 | 1,184 |
| Test 04 | 0,611 | 0,054 | 0,494 | 0,557 | 0,632 | 0,644 | 0,926 |
Der Durchschnittswert (= arithmetisches Mittel) zeigt uns: die Optionen bei Cache- und Index-Management wirken sich beträchtlich auf die Geschwindigkeit des Imports aus.
- In Test 01 ist das Caching deaktiviert und die automatische Indizierung aktiviert. Das Testsystem benötigt 1,133 Sekunden pro Produkt.
- Bei Test 02 wird die automatische Indizierung deaktiviert. Siehe da, der Import benötigt nur noch 0,858 Sekunden. Das entspricht einem Minus von 24,27 Prozent! Der Import läuft beinahe um ein Viertel schneller ab.
- Wird wie in Test 03 stattdessen das Caching aktiviert, sinkt die durchschnittliche Zeit auf 0,773 Sekunden. Wir ersparen uns 31,77 Prozent der Zeit, der Import dauert ca. ein Drittel kürzer als bei der Standardeinstellung.
- In Test 04 kombinieren wir die Einstellung: die automatische Indizierung ist deaktiviert, das Caching aktiviert. Jedes Produkt wurde durchschnittlich in 0,611 Sekunden eingefügt. Wir sparen uns somit 46,07 Prozent der Importdauer – nicht schlecht, wenn man bedenkt, dass die Konfigurationsänderung in einer halben Minute erledigt ist.
Die Standardabweichung zeigt, dass sich die Schwankungen beim Import in Grenzen halten. Wir können uns die obigen Kennzahlen in einem Boxplot ansehen, da die Zahlen so wesentlich anschaulicher werden:
Die Whisker (= Striche) zeigen in diesem Fall den schnellsten bzw. langsamsten Import. Da einzelne Ausreißer (wie z.B. bei Test 01) das Bild dadurch etwas verzerren, orientieren wir uns lieber an den Boxen. Die Box repräsentiert die mittleren 50 Prozent der Produkt-Inserts. In diesem Bereich liegt die Import-Geschwindigkeit die Hälfte der Zeit. Auch hier sieht man, dass das Caching einen größeren Vorteil als die manuelle Indizierung bringt, man aber nach Möglichkeit beide Einstellungen vornehmen sollte. Gerade für den initialen Import bzw. die Entwicklung bietet sich das an.
Fazit
Wir haben getestet, wie sich die Einstellungen des Index- und Cache-Managements in Magento 1.4.1.1 auf den Import simpler Produkte via SOAP-API auswirken. Die wichtigsten Erkenntnisse:
- Der Import mit manueller Indizierung spart ca. 25% der Zeit.
- Der Import mit aktiviertem Caching spart ca. 33% der Zeit.
- Der Import mit manueller Indizierung und aktiviertem Caching spart fast 50% der Zeit.
- Wenn möglich sollte man beide Einstellungen vornehmen. Das bietet sich v.a. beim initialen Produktimport an, wenn der Shop noch nicht im Livebetrieb ist. Bei der manuellen Indizierung darf man nicht vergessen, nachher die Reindizierung anzustoßen!
Falls jemand von euch ähnliche Tests vorgenommen hat, bin ich sehr an den Ergebnissen interessiert. Wie verhält sich der Import mit realitätsnahen Daten, größeren Datenbeständen und/oder Optimierungen der Server-Konfiguration? Wie groß ist der Unterschied zwischen verschiedenen Magento-Versionen? Da nur Gott weiß, wann ich Zeit für ausführliche Tests finde, wäre es toll, wenn das schon jemand für mich erledigt hat. ![]()
Tweets that mention Blogpost von Matthias: Magento SOAP-API: Performancetests zum Produkt-Import – #limesoda — Topsy.com am 14.11.2010
[...] This post was mentioned on Twitter by Webmaster Blog, LimeSoda. LimeSoda said: Blogpost von Matthias: Magento SOAP-API: Performancetests zum Produkt-Import – http://ow.ly/39wyi #limesoda [...]
Links 48/2010: Magento, e-Commerce, Mobile und Qualitätssicherung | Matthias Zeis am 5.12.2010
[...] Beispiel habe ich vor einiger Zeit darüber geschrieben, dass sich durch manuelles Indizieren die Geschwindigkeit des Produkt-Imports steigern lässt. Mit Ivans Erklärung, wie man Indizes per Code auf automatische/manuelle Aktualisierung [...]
Witi am 16.12.2010
Hallo,
einen Performancetipp hätte ich für dich: Parallele SOAP-Anfragen.
Wir fahren in einem Projekt bis zu acht parallele SOAP-Requests an Magento. Läuft erstaunlich gut. Steigert bemerkenswert die Performance, die Last des Servers geht allerdings logischerweise hoch.
In den parallelen Request sollte man allerdings keine unterschiedlichen Operationen (z.B. Produktattribute und Staffelpreise des Produktes aktualisieren) an einem Produkt durchführen. Das hat in einigen seltenen Fälle zu “duplicate keys” Fehlern bei INSERT-Statements geführt.
VG Witi
Matthias Zeis am 16.12.2010
Hallo Witi,
danke für den Tipp! Das machen wir bisher nicht, klingt interessant und testenswert. Die Alpha von 1.5.0.0 steht auch schon unmittelbar vor der Tür – wer weiß, vielleicht hat sich das leidige Thema dann bald einmal erledigt.
lg Matthias
Robert Erlinger am 10.1.2011
Ich habe gelesen, dass in diesem Test nur die V1 der Magento SOAP API verwendet wurde. Gibt es auch einen Test der V2, bzw. ist dafür einer geplant?
Wäre echt interressant, da wir planen bei einem Projekt die V2 zu nutzen.
LG,
Robert
Matthias Zeis am 10.1.2011
Hallo Robert,
die V2 habe ich nicht mit demselben Skript getestet, das wäre aber sicher einen Test wert. An sich sollten (vor allem mit Caching) keine großen Unterschiede bemerkbar sein, da der Code von V1 und V2 bei der Produkterstellung sehr ähnlich ist.
lg Matthias
vansiegmund am 21.1.2011
Hallo Matthias,
ich bin neu im PHP5 und Magento Umfeld und dein Blog hilft mehr sehr Fahrt aufzunehmen
Deine Infos zur Magento Import API sind wirklich gut. Jedoch habe ich eine Frage:
Wie kann ich die customer group per API in den Shop einspielen?
Der Hintergrund sind Kundenspezifische Preise via Tier Price.
Scheinbar geht es in diesem Shopsystem nicht anders Kundenspezifische Artikelpreise abzubilden, als über das matching via Kundengruppe.
Wird bestimmt der DB-Hammer, wenn ich plötzlich pro Artikel bis zu 10.000 individuelle Gruppenpreise habe
Gibt es vill. eine bessere Lösung jedem Customer pro Artikel einen individuellen Preis anzubieten?
Viele Grüße
Matthias Zeis am 21.1.2011
Hallo,
danke für die Blumen!
Ich fürchte, dass du für die Synchronisierung der Kundengruppen eigene API-Aufrufe implementieren musst (einen Einstieg gibt es im Magento-Wiki. Branko Ajzele hat im letzten Jahr angekündigt, die vorhandene API zu erweitern (Projekt mapy) – du könntest bei ihm nachfragen, ob er das Projekt abseits von git vorangetrieben hat.
10.000 individuelle Gruppenpreise pro Artikel sind allerdings ein Hammer.
Eine Möglichkeit wäre, die Produkt-Model-Klassen zu erweitern und sich beim Laden bzw. Speichern des Produkts (oder auch bei getPrice(), setPrice() etc.) einzuschalten, um dort den Preis zu manipulieren. Hier kannst du dann eine andere Quelle für die Preise heranziehen oder den Preis berechnen, wenn sich die Preise aus einem fixen Schlüssel ergeben.
Auf diese Weise kann man die Handhabung der Artikelpreise stark verändern. Wir haben z.B. bei einem Webshop konfigurierbare Produkte mit zahlreichen simplen Produkten als Varianten umgesetzt, wobei jede Variante eigene Staffelungen und Staffelpreise besitzen kann und jeder Preis absolut festgelegt wird (nicht als Aufpreis/Abschlag gegenüber einem Standardpreis). Die Preistabellen werden dann dynamisch möglichst kompakt dargestellt. Für das konfigurierbare Produkt wird ein bestimmter “ab”-Preis als Preis berechnet.
Möglichkeit 2 wäre, im Hintergrund einen Request für Kunden X und Produkt Y an die Warenwirtschaft zu schicken. Meiner Erfahrung nach können die Wawis so einer Anfragenflut aber normalerweise nicht Herr werden. Der Ansatz ist nur zu überlegen, wenn bei der Preisbildung so viele Spezialfälle vorliegen, dass die doppelte Umsetzung (Wawi und Shop) unverhältnismäßigen Aufwand verursacht.
Ich hoffe, die Antwort konnte ein wenig helfen. Für Fragen stehe ich natürlich zur Verfügung!
lg Matthias
vansiegmund am 21.2.2011
Hallo Matthias,
danke sehr für diese Antwort, Sie hat mir sehr geholfen ^^
Das Thema” Performancetests zum Produkt-Import” habe ich ebenfalls ausgiebig behandelt und habe eine Frage zu “manuelle Indizierung und aktivierter Caching”:
Kann ich sciptseitig via PHP call eine Re-Indizierung nach dem soap Import veranlassen?
Hintergrund ist, dass ich nächtlich einen Import fahre, zuvor werden alle notwendigen Tabellen per TRUNCATE geleert.
Hast du diesen Test schon mit der neuen Version 1.5.0.0 etc. durchgeführt?
Ich werde gleich einen Test fahren
Bei der Version 1.4 habe ich für 2.640 simple products:
real 28m57.434s
user 0m0.560s
sys 0m1.740s
benötigt, ist schon mal ein Anfang, trotzdem noch viel zu lang für den Live-Betrieb.
Danke & Gruß
Matthias Zeis am 21.2.2011
Hallo,
gerne doch! Tests mit der stabilen Version 1.5 habe ich noch nicht durchgeführt, aber es wird definitiv Zeit dafür.
Du kannst eine Re-Indizierung über ein PHP-Skript oder direkt über ein Shell-Skript veranlassen.
Das PHP-Skript:
< ?php // zuerst mit Mage::app() initialisieren $processCollection = Mage::getSingleton('index/indexer')->getProcessesCollection(); $processCollection->walk('reindexAll');In einem Shell-Skript kannst du shell/indexer.php verwenden:
Simone am 11.2.2011
Hi guys,
I saw this interesting article but unfortunately I read it translated with Google Translator…
I’ve got an issue using the API to import product and maybe you can help me solve it. I’m using Magento 1.4 and when I use the API to import products, they aren’t added to the normal product catalogue area, but they appear in “TheFind Feed” menu just under “Catalogue > Products”. Can you share (or send me by email) the code you use to add products using APIs?
Thanks a lot.
Matthias Zeis am 11.2.2011
Hi Simone,
which type of products are you trying to add (simple products, configurable, …)? Did you assign the products to a website, a store, a category and so on?
Maybe it would be easier if you send your code and we try to find the error in it.
Best regards, Matthias
Simone am 11.2.2011
Hello, thanks a lot!
Here is my code…
$newProductData = array(
'name' => (string) $prodotto->descrizione,
'websites' => array(1),
'short_description' => (string) $prodotto->descrizione,
'description' => (string) $prodotto->descrizione,
'price' => (string) $prodotto->prezzo
);
try {
$newProductId = $soap->call($sessionId, 'catalog_product.create', array('simple', $set_id, $codice, $newProductData));
} catch (SoapFault $e) {
echo "Product added: " . $e;
}
Matthias Zeis am 12.2.2011
Hello,
the problem is that the API example code is somewhat incomplete. The code below should display the product in “Catalog” > “Manage Products”. Notice that the product still isn’t assigned to a category!
$productData = array( 'name' => 'name of product', 'websites' => array(1), // array(1,2,3,...) 'short_description' => 'short description', 'description' => 'description', 'price' => 12.05, 'status' => 1, // Product is active 'stock_data' => array( 'is_in_stock' => 1, // In stock 'qty' => $qty, // Quantity ), 'tax_class_id' => 1, // Tax class 'visibility' => 4 // Product is visible in catalog and search ); try { $productId = $proxy->call($sessionId, 'product.create', array('simple', $attributeSetId, $sku, $productData)); echo "Product inserted: " . $productId; } catch (Exception $e) { echo "Error: " . $e->getMessage(); }