Nudge am 28.01.2013

DB-Performance von Zend

in MySQL, PHP | Tags: MySQL, Performance, PHP, Zend

Ich wollte das schon immer mal loswerden, habe aber bisher nie die Zeit dazu gefunden: Zend_Db und vor allem Zend_Db_Table  sind lame. So. Früher habe ich schon oft überlegt, warum tust du dir diesen ganzen SQL-Kram immer noch an? Es gibt doch angefangen von PDO auch so bequeme Frameworks wie Zend, CakePHP oder Symfony mit Doctrine. Niemand muss doch mehr diesen Quatsch selbst hacken. Aber am Ende steht manchmal noch die Frage im Raum, ob man nun eine komplexe Infrastruktur benötigt oder einfach mal ein bisschen schnelleren Code verwenden sollte. Zu der Zeit, als ich die Überlegungen traf, kam es halt (in meiner Situation) auf jede Millisekunde an.

Nun aber zurück zum Thema. Wie lame ist es denn genau? Weil ich das immer wieder vergesse, habe ich dazu in einem kleinen Test verschiedene Select-Queries gegeneinander antreten lassen und nun ist die Zeit reif, dies hier zu dokumentieren, damit ich das selbst beim nächsten Mal schneller finde. Zugegebenermaßen alles absolut hemdsärmelig und auf die Schnelle auf dem eigenen Laptop. Aber für den ersten Vergleich sollte es reichen.

Und hier auch das Resultat des Tests: Am langsamsten sind die One-Man-Show-Select-Queries per

1
$table->find(1234);
$table->find(1234);

Das heißt, man benötigt ein oder mehrere Ergebnis-Sets, hat aber noch keine Meta-Daten abgerufen. Also wird Zend erstmal ein

1
DESCRIBE <table>;
DESCRIBE <table>;

an die DB senden und die empfangenen Daten in schöne Arrays, zB. $_metadata umsetzen und erst dann das eigentliche Select-Query absetzen. Das dauert bei mir (langsamer alter Rechner) etwa 4,5-5,0 Millisekunden.

Man könnte auf die Idee kommen, per

1
$table->info();
$table->info();

die Metadaten vorher einfach selbst abzurufen. Aber denkste! Beim nächsten $table->find(); wird Zend das schön selbst noch einmal durchführen. Vertrauen ist gut, doch Kontrolle ist immer besser.

Das folgende Select-Query, auch wieder per $table->find(); benötigt dann aber “nur” noch 1,1-1,2 Millisekunden, aber recht konstant.

Doch was könnte ich nun noch besser (schneller machen)? Ich könnte Zend_Cache verwenden, um die Metadaten nicht erst von der Datenbank zu holen, aber dann sind die folgenden Queries auch in der gleichen Performance-Klasse. Hier verlasse ich also das ZF-Framework wieder ein Stück und hacke den Select-Query direct am Db-Objekt hinein, so wie es die ZF-Doku selbst vorschlägt:

1
$db->query('SELECT * FROM <table> WHERE id=1234')->fetchAll();
$db->query('SELECT * FROM <table> WHERE id=1234')->fetchAll();

Wow! Da kommt man immerhin bis etwa 0,35-0,4 Millisekunden. Was ist da denn noch zu tun? Zunächst muss ja das Statement-Objekt gebildet werden und vermutlich sind auch eine ganze Reihe PDO-Objekte im Aufruf-Stack involviert, bis die gesammelten Werke übergeben werden.

Fast-Forward

Und nun zum eigentlichen Vergleich: Wenn man mit den “nativen” mysql-Funktionen von PHP arbeitet, dann erreicht man mit diesem Query eine Traumzeit von 0,09 Millisekunden. Ich muss nativ deswegen in Anführungszeichen schreiben, weil es ja bis dato immer noch ein Manko ist, dass PHP die Daten per libmysql doppelt umkopiert und gar nicht so nativ ist. Das kann also nur noch schneller werden. Wenn man diese Aufrufe übrigens selbst in schöne Objekte für das Model und die DB kapselt, um ähnlich bequeme Befehle wie im ZF zur Hand zu haben, ist ein messbarer Overhead trotzdem kaum vorhanden. Vermutlich ist einfach der Zoo von Objekten mit Zend/PDO so groß, das kann man selbst gar nicht nachbauen.

Fazit

Wenn man zum Beispiel davon ausgeht, dass die Gesamtantwortzeit eines wichtigen Systems unter 200 ms liegen sollte und man bereits eine Netzwerk-Latenz von zweimal 60 ms im Schnitt hat, dann bleibt nicht mehr viel Zeit für die Berechnung der Site auf dem Server übrig. Gerade solche Systeme wie Shops, die erstmal eine ordentliche Anzahl von Tabellen benötigen, um nur einen Artikel oder eine Kategorie mit Stammdaten zu versorgen und daneben auch noch den Warenkorb, letzte angesehene Artikel, Up-Selling, Cross-Selling und noch ein bißchen Community an den Mann bringen wollen, müssen mit Statements sparsam umgehen. 5 ms für ein Query wäre hier definitv zuviel. Im Bereich von 1 ms könnte man noch ein paar Statements absetzen, aber die wirklich teuren Operationen wie Updates kommen ja noch dazu. Insofern bleibt wie immer abzuwägen, ob man die Zeit der ersten schnellen und bequemen Entwicklung mit einem Framework wie Zend nicht irgendwann wieder in Profiling und Optimierung wieder investieren würde, wenn der Server ächzt und die Besucher seufzen.

Hier die Ergebnisse noch einmal in Reinform:

1
2
3
4
5
6
0.004565 - $table->info();
0.004936 - $table->find(); - erster Aufruf
0.001227 - $table->find();
0.001137 - $table->find();
0.000352 - $db->query()->fetchAll();
0.000091 - mysql_query();
0.004565 - $table->info();
0.004936 - $table->find(); - erster Aufruf
0.001227 - $table->find();
0.001137 - $table->find();
0.000352 - $db->query()->fetchAll();
0.000091 - mysql_query();

Vielleicht hat der ein oder andere aber auch ganz andere Tricks drauf, um die Performance in die Höhe zu schrauben. Wenn ja, würde ich mich sehr freuen, diese kennenlernen zu dürfen. Schließlich ist so ein objektorientierter Abruf auch was ganz schickes.

 

 


Das mark ich mir: Alltagz Mr Wong Yigg Del.icio.us Yahoo MyWeb Blinklist Google folkd
 

One response to “DB-Performance von Zend”

  1. DonTermi says:

    Cache doch einfach die Meta Daten der Tabellen 🙂 In der Bootstrap ganz einfach zu erledigen per

    protected function _initCache()
    {
    /* @var $cm Zend_Cache_Manager */
    $cm = $this->bootstrap(‘cachemanager’)->getResource(‘cachemanager’);
    Zend_Db_Table_Abstract::setDefaultMetadataCache($cm->getCache(‘db’));
    }

    Solltest eben nur memcached Daemon mit dem php Modul memcache installiert haben. Das Problem in der Performance liegt nämlich darin das bei jedem Seitenaufruf Zend_Db die Metadaten der Tables ausliest. Kommen die Meta Daten aus dem Cache geht es dafür um so schneller.

Leave a Reply

Your email address will not be published. Required fields are marked *