Beiträge

OXID EE MySQL Master/Slave Setup

oxid-news

Hintergrund

Der OXID eShop unterstützt in der Enterprise Edition (EE) MySQL Master/Slave Datenbank-Setups, was für sog. „Read-/Write-Splitting“ genutzt werden kann. Dadurch ist es möglich, Datenbank-Abfragen auf mehrere Datenbank-Server zu verteilen, um damit im Idealfall an Datenbank-Performance zu gewinnen und Shops gerade zu Lastzeiten schneller zu machen.
Read-/Write-Splitting kann ebenso helfen, das Shop-Frontend performant zu halten, wenn z.B. gerade Massen-Updates über ERP- oder PIM-Schnittstellen im Shop-Backend laufen, welche mit vielen Insert-Statements nicht selten Datenbank-Locks auslösen, die sich dann auch auf das Frontend auswirken können. Im schlimmsten Fall ist der Shop zeitweise nicht mehr aufrufbar, weil gleichzeitig zu den laufenden Insert- und Update-Statements aus einer gelockten Datenbank-Tabelle gelesen werden soll.

Master/Slave Setup in OXID

Im OXID eShop ist die Konfiguration der Master-/Slave-Verteilung sehr einfach, hier genügt es, in der allgemeinen Shop-Konfigurationsdatei „config.inc.php“ die einzelnen Server einzutragen.

Hierzu gibt es zwei Variablen, die gesetzt werden müssen:

$this->aSlaveHosts = array('localhost', '192.168.0.10:3306', '192.168.0.11:3307');
$this->iMasterSlaveBalance = 0;

Im Array „aSlaveHosts“ können beliebig viele IP-Adressen (mit optionalem Port) eingetragen werden. Auch der Master wird hier als erster Wert hinterlegt.
Der zweite Parameter, „iMasterSlaveBalance“ legt fest, wie die Verteilung der Abfragen zwischen Master und Slave ist.
Ein Wert von „0“ bedeutet hier, dass alle Abfragen auf den Slaves stattfinden und auf den Master nur schreibend zugegriffen wird.

Für Shops mit regelmässig laufenden Artikeldaten-Updates über Schnittstellen zu Warenwirtschaftssystemen usw. sollte diese Einstellung verwendet werden, damit sichergestellt ist, dass auch bei Massen-Updates die Lesezugriffe aus dem Shop-Frontend nicht in Mitleidenschaft gezogen werden.

Das Setup der Datenbankserver an sich geht über den Umfang dieses Artikels hinaus, hierzu finden sich z.B. sehr gute Infos unter https://www.thomas-krenn.com/de/wiki/MySQL_Replikation

Tipps und Tricks für das MySQL Master/Slave Setup

Der OXID EE Shop selbst ist bereits sehr gut für die Master-/Slave-Aufteilung optimiert, sobald Slaves konfiguriert sind, hält sich der Shop strikt an die definierte Aufteilung und führt z.B. Lesezugriffe immer auf den Slaves aus.

Für eigene Erweiterungen des Shops sollte man sich hier am OXID Standard orientieren, hier ist es v.a. wichtig, für Lese- oder Schreibzugriffe die richtigen Methoden des OXID Datenbank-Adapters zu verwenden.

Prinzipiell gilt: für Lesezugriffe sollte man immer die „select()“-Methode der „oxDb“-Klasse nutzen, da nur diese wirklich standardmässig dezidiert auf die Slaves zugreift.

$oRs = oxDb::getDb()->select("SELECT * FROM oxarticles WHERE oxartnum = '12345'"); // liest vom Slave

Man kann allerdings als dritten Parameter „false“ übergeben, um dennoch vom Master zu lesen – Standard ist jedoch „true“ und damit „Slave:

public function select($sSql, $aParams = false, $blType = true)

Auch die Methoden „getOne()“, „getArray()“, „getAssoc()“, „getRow()“ und „getAll()“ selektieren standardmässig von den Slaves, können aber ebenfalls per Parameter auf den Master „umgebogen“ werden bei Bedarf.

Nutzt man hingegen die Methoden „execute()“ oder „query()“, geht das SQL-Statement ausnahmslos an den Master-Server!

$oRs = oxDb::getDb()->query("SELECT * FROM oxarticles WHERE oxartnum = '12345'"); // liest vom Master

Problematisch sind bei Master-/Slave-Setups oft SQL-Fehler, welche zu Fehlermeldungen auf Datenbank-Ebene führen. MySQL ist hier meist recht rigoros und beendet die Synchronisation zwischen Master und Slaves. Diese muss dann vom System-Administrator wieder manuell aktiviert werden.

Man sollte das System also erst auf Herz und Nieren testen und die Synchronisation im Testbetrieb beobachten, um zu entscheiden, ob es hier kritische Stellen im Shop gibt und regelmässige SQL-Fehler auftauchen.

Handelt es sich um temporäre, „unwichtigere“ Daten (z.B. Session-Einträge, Captcha-Codes o.ä.), kann man ggf. in Erwägung ziehen, die betroffenen Tabellen aus der Synchronisation herauszunehmen bzw. MySQL-Fehler für diese Tabellen bei der Synchronisation zu ignorieren, siehe z.B. https://dev.mysql.com/doc/refman/5.7/en/replication-options-slave.html#option_mysqld_slave-skip-errors
Ausserdem kann helfen, die Slaves explizit als „read_only“ zu konfigurieren, damit es keine „versehentlichen“ Schreibversuche auf die Slaves gibt.

MySQL Master/Slave in OXID – Fazit

Mit der Option für Master-/Slave-Setups bietet OXID gerade für große Shops mit regelmässigen, umfangreichen Datenänderungen über Drittsysteme einen erheblichen Performance-Hebel.
Weiss man um die technischen Hintergründe des OXID-Datenbankadapters und kennt die möglichen Fehlerquellen auf Seiten der MySQL-Datenbank-Synchronisation, spricht nichts dagegen, dieses Feature im produktiven Einsatz zu nutzen.
Hat man dann noch einen technisch versierten Hoster an seiner Seite, kann eigentlich nichts mehr schiefgehen :)

Hoffentlich hat dieser Artikel dazu beigetragen, die technischen Hintergründe in Bezug auf OXID EE und das Mysql Master-/Slave-Setup zu erhellen!

 

Blog Einträge

Under the hood: OXID eShop EE Caching und die SOAP-Schnittstelle

Über das Caching in der OXID EE gab es an dieser Stelle ja schon einige Informationen. So ein Cache, egal ob auf Datei- oder z.B. Memcached-Basis, sollte natürlich möglichst lange vorhalten, um optimale Performance zu gewährleisten.

Im OXID eShop gibt es zahlreiche Aktionen, die ein (teilweises) Leeren des Caches zur Folge haben. Neben Verkäufen im Shop und damit verbundenen Lagerbestandsänderungen usw. können dies auch diverse Aktionen im Shop-Administrationsbereich sein. So empfiehlt es sich im Normalfall generell, im Shop-Admin unter „Performance“ die Checkbox „Cache nur beim Ausloggen aus dem Administrationsbereich leeren“ („blClearCacheOnLogout“ im Code) zu aktivieren, um ein Löschen des gesamten Caches zu verhindern, wenn man im Admin z.B. Shopeinstellungen bearbeitet, Benutzerdaten ändert usw. In der OXID EE muss dies natürlich pro Mandant aktiviert sein. Ein weiterer „Cache-Killer“ kann übrigens die Performance-Einstellung „Ähnliche Artikel anzeigen“ sein – hier wird im Falle eines Artikel-Verkaufs im Shop ebenfalls großzügig der Cache geleert, wenn es ähnliche Artikel (zu einem definierbaren Prozentsatz übereinstimmende Attribute) im Shop gibt.  Wenn man dieses Feature nicht zwingend benötigt und den EE Cache nutzt, sollte man darauf ggf. verzichten und die Checkbox deaktivieren. Weiterlesen

Oxid Enterprise Caching

Der Oxid eShop bietet in der Enterprise Version eine ausgefeilte und flexible Caching Strategie an. Es gibt die Möglichkeit, verschiedene Caching-„Backends“ zu nutzen bzw. auch eigene Backends zu implementieren. Gecached werden können theoretisch sämtliche Shop-Klassen, um z.B. konkret Startseite, Kategorieübersichten oder Detailseiten usw. zu cachen und damit als (weitgehend) „statisches“ HTML auszuliefern, um die Shop-Performance zu steigern.

Die gewünschten Klassen können im Shop-Admin unter „Stammdaten – Caching“ eingetragen werden. Standardmäßig ist im Shop als Caching-Backend der „OXID eShop Disk Storage Cache“ verfügbar, der die Cache-Daten als „.cache“-Dateien im „/tmp“-Verzeichns des Shops ablegt und eine Referenz darauf in der „oxcache“-Datenbanktabelle einträgt. Hat man auf dem Server die „Zend Plattform“ installiert, so steht auch ein Zend Cache-Backend zur Verfügung. Es ist aber auch problemlos möglich, eigene Backends einzusetzen, die lediglich ein bestimmtes Interface implementieren müssen und damit Methoden zu bieten, Daten im Cache zu speichern, wieder auszulesen und schliesslich auch wieder zu löschen. Exemplarisch sei hier ein „Memcached„-Backend genannt, das Shoptimax für einen Kunden umgesetzt hat, um das Caching effizienter zu machen und für verteilte Shop-Server (Loadbalancer/Cluster) mit verteilten tmp-Verzeichnissen zu optimieren – hier hat man sonst eine hohe sog. „Cache Miss“-Rate (und dadurch letztlich eine schlechtere Performance), da die „.cache“-Dateien zwar in der Datenbank noch referenziert werden, teilweise aber im Dateisystem nicht gefunden werden, da sie unter Umständen nicht auf demselben Cluster-Server im dortigen „tmp“-Verzeichnis des Shops liegen, auf dem auch der Shop-Kunde aktuell mit dem Browser „gelandet“ ist.

Nicht immer sollen jedoch komplette Shop-Seiten gecached werden, Informationen wie Warenkorbinhalt, Login-Name, Verfügbarkeitsanzeigen usw. sollen natürlich immer aktuell sein. Zu diesem Zweck „injiziert“ der Oxid EE Shop in die gecachten Seiten dynamische „Snippets“, dafür sorgt die Smarty-Funktion „oxid_include_dynamic„. Diese inkludiert Smarty Template-Schnippsel (normalerweise aus dem „out/basic/tpl/dyn“ Verzeichnis) in die gewünschte Seite bzw. das gewünschte Template. Hier ein Beispiel das die Warenkorbinhalte dynamisch einbindet:

[{oxid_include_dynamic file=“dyn/mini_basket.tpl“ type=“basket“ extended=true testid=“RightBasket“}]

Wichtig ist hierbei, dass an ein solches „dynamisches“ Template kein Objekt als Argument übergeben werden kann, wie dies bei normalen Templates der Fall ist, also z.B. ein “ … product=$product …“ o.ä ist hier nicht möglich. Stattdessen muss in einem solchen Fall z.B. ein String oder Integer-Wert übergeben werden wie etwa die Produkt-Id, im Beispiel also etwa “ … productid=$product->oxarticles__oxid-value …“. Hintergrund ist, dass die Parameter-Werte per base64_encode() und base64_decode() umgewandelt und übertragen werden. Um den übergebenen Parameter dann auszuwerten und z.B. das gewünschte Objekt zu erzeugen/laden und wiederum im Template zur Verfügung zu stellen, ist es nötig, die Funktion oxCache::_processDynContent() per Modul zu überschreiben und mit der gewünschten Logik zu erweitern. Um auf die übergebenen Parameter direkt im inkludierten, dynamischen Template zugreifen zu können, muss man zudem einen Unterstrich (Underscore) „_“ davor setzen, im Beispiel also [{$_productid}], da in der Methode oxCache::_processDynContent() alle Variablen mit diesem Prefix versehen und wieder in das Template geschrieben werden.
Will man aus irgendwelchen Gründen Template-Logik nur bei aktivem Caching einbinden, kann man die Variable $_render4cache abfragen, die in diesem Falle in allen Templates verfügbar ist:

[{if $_render4cache}]
    Sie sehen eine gecachte Version der aktuellen Seite!
[{/if}]

Fazit: Für größere Shops mit sehr vielen Kategorien und/oder Artikeln ist Caching im OXID eShop eigentlich unerlässlich, wir konnten in manchen Shops und mit bestimmten Shop-Klassen eine Performance-Steigerung um den Faktor 5 feststellen – nicht nur im Hinblick auf Google-Ranking von Vorteil, sondern generell für die Usability und Akzeptanz des Shops durch die Kunden.