Monday, 19. April 2010
Wer wie wir öfter mal mit MySQL zu tun hat, kommt irgendwann um das Thema Optimierung nicht mehr herum. Doch irgendwann ist der gesamte RAM verbaut, die stärkste CPU eingelötet und die schnellst drehenden Festplatten eingesteckt, dennoch ist die Applikation irgendwie langsam
Meist kommt man dann zu dem Schluss, dass der User und seine Applikationen (bzw. dessen verwendete SQL-Query's) das eigentliche Problem sind  . Natürlich hat man nicht immer die Zeit, jeden einzelnen Query in den Scripten seiner Kunden zu suchen. Natürlich gibt es auch dafür ein passendes Tool, welches zu meinem Erstaunen sogar neben Windows und Linux auch unter Mac OSX läuft. Jet Profiler for MySQL heißt das Ganze und kommt von der Firma Polaricon AB.

Der eigentliche Nutzen des Tools ist es, etwas langsamer laufenden SQL-Queries, sogenannte Slow-Query's, zu finden, um diese eliminieren zu können. Als nebenläufige Features gibt es noch einige grafische Auswertungen, sowie Tabellen und Graphen. Die langläufigsten Queries zeigt der Profiler dazu im unteren Teil der App an. Diese werden nach der längsten Laufzeit aufgeschlüsselt. Hier erkennt man auch gleich den Unterschied der freien zur kostenpflichtigen Version. Die freie zeigt nur den 1 sowie den 5-x langsamsten Query. Nummer 2, 3, 4, 5 sieht man nur in der vollen Version. Außerdem zeigt die Vollversion auch gelockte Queries, sowie die Top-User und die Top-States. Aber auch die freie Version genügt für den ersten Überblick vollkommen, man kann ja Query für Query abarbeiten  Klickt man auf das Explain-Feld hinter dem Query geht ein seperates Fenster auf, welches ein EXPLAIN auf das Query anwendet und diesen noch einmal grafisch auswertet. Das Tool gibt aus ob das erklärte Query gut oder schlecht aufgebaut ist (Punkte/Flaggen-Wertung, sowie Wertungen very good/very fast bis very bad/very slow  ).
Mein Fazit: Ein gutes Tool, um einfach und schnell langsame Querys finden zu können und einen Überblick über die Statistiken des MySQL-Servers bekommen zu können. Ein dickes Plus ist natürlich, dass das Tool out of the Box auch unter Linux und Mac OSX läuft.
Wednesday, 24. March 2010
Derzeit ist ja NoSQL im Trend, und das wohl zu Recht. Bei vielen Projekten ist eine relationale Datenbank einfach nur Overkill, da oft stupide immer wieder die gleichen Datensätze abgefragt werden und die Möglichkeiten einer relationen DB nur zu einem Bruchteil verwendet bzw. benötigt werden. Aus diesem Grunde haben wir uns einmal intensiver mit den aktuellen NoSQL-Engines beschäftigt und sind zu überraschenden Ergebnissen gekommen.
Als Testumgebung diente eine VMware VM bzw. anschließend ein Dedizierter-Server (Quad-Opteron, 8 GB RAM), um noch besser die echte Performance messen zu können. Wir haben mit einem kleinen Java-Programm immer mehrere 100k bzw. millionen Datensätze (je nachdem, was so reinpasste  ) geschrieben und wieder ausgelesen, jeweils mit nur einem Key in Form eines Integer. Ein kompletter "Datensatz" bestand aus jeweils so um die 200 Byte in Form eines Zufalls-String. Die Tests wurden auf zwei via GBit zum Server verbundenen MacBook's Pro durchgeführt. Die Reads wurden meist mit 30-40 Threads zu je 500-800 Datensätzen ausgeführt, um halbwegs eine echte Situation mit mehreren Clients nachzustellen.
Als erstes war Cassandra dran, wovon wir uns viel erhofften. Denn Digg, Facebook, Twitter und Andere können ja nicht irren. Oder etwa doch? Ehrlichgesagt waren unsere Tests mehr als ernüchternd. Sowohl mit Java, PERL und auch PHP schafften wir nicht mehr als 100 Writes pro Sekunde, egal wie wir es anstellten (batched, einzeln). Auf dem Server selbst gab es komischerweise keinerlei CPU- oder I/O-Last. Wir machen daher das Thrift-Interface für die schlechte Performance verantwortlich. Thrift ist ein Code-Generator, der auf Wunsch aus einem Template für viele Programmiersprachen API's baut. Das ist zwar sehr angenehm und schick, aber offenbar langsam. Abschließend konnten wir allerdings nicht gänzlich klären, warum Cassandra bei uns versagt hat. Auch die durchaus freundliche Mailingliste konnte auf unsere Anfrage hin das Problem nicht klären. Fakt scheint, dass Cassandra erst bei vielen Clients aufblüht. Wenn man von 100 Clients aus tatsächlich jeweils 100 Writes/sek schafft, sieht das Ergebnis schon wieder besser aus. Erwähnen sollte man vielleicht an dieser Stelle auch noch, dass Cassandra und Thrift relativ schlecht dokumentiert sind, was sicher auch an den Versionen 0.x liegt. Die Reads haben wir aufgrund des ernüchternden Write-Performance gar nicht erst getestet, sorry..
Memcached darf in dieser Runde natürlich nicht fehlen, zumal wir damit bereits gute Erfahrungen im Produktiveinsatz gemacht haben. Unser Java-Testprogramm schaffte hier 1000 Writes/sek und ganz ordentliche 10k Reads/sek. Was bei Memcached auffällt, ist die nahezu nicht vorhandene CPU-Auslastung, egal wie sehr man den Server quält. Diese Tatsache und auch Erfahrungen aus der Praxis zeigen, dass sehr viele Connects und gleichzeitige Abfragen wunderbar schnell beantwortet werden könnne. Problematisch ist natürlich, dass kein Backup vorliegt, wenn der Server abstirbt. Es gibt zwar einige Tools/Scripte, die einen Dump erlauben, getestet haben wie dies allerdings nicht.
Diese Lücke füllt MemcacheDB - hier werden die Daten zusätzlich zu der Kopie im Speicher noch in eine Berkeley DB geschrieben. Leider ist das letzte MemcacheDB-Release von 2008. Ob das nun daran liegt, dass die Software perfekt funktioniert oder ob die Entwickler keine Lust mehr haben, kann man nur erahnen. Wir mussten zumindest Berkeley DB 4.7 von Hand kompilieren, da die MemcacheDB nur damit zusammenarbeiten wollte. Aktuelle wäre Berkeley DB 4.8. Die Ergebnisse waren nicht direkt schlecht, wir schafften 600 Writes/sek und um die 5k Reads. Da die Daten eben noch sicher auf Platte geschrieben werden, kann die Performance nicht mit Memcached mithalten. Natürlich merkt man auch die Arbeit auf der Platte entsprechend an der Serverlast, die Werte waren aber im Rahmen.
Redis war ebenfalls einen Blick wert und beeindruckt mit dem wohl größten Funktionsumfang. Die Command Reference liest sich beeindruckend, teilweise schon fast wie die eines "echten" Datenbankservers. Ebenso beeindruckend sind eigentlich auch die Testergebnisse: 30k Writes und Reads pro Sekunde. Allerdings war die CPU hier auch ziemlich gut ausgelastet, wenn man viele gleichzeitige Zugriffe simuliert. In der Config kann man bestimmte Intervalle festlegen, in denen ein Dump auf die Platte geschrieben wird, z.B. "alle 300 Sekunden, wenn sich in dieser Zeit mindestens 1000 Datensätze verändert haben". Das macht einen Restore im Fehlerfall problemlos möglich.
Zu guter Letzt kam ein von einem netten Kollegen geschriebenes Java-Programm zum Einsatz. Es lauscht auf einem Port und nimmt dort Anfragen entgegen, nachdem es vorher über einen anderen Port mit Daten gefüttert wurde. Die Daten werden im Speicher in Form einer Hasttable gehalten. Als Key dient auch hier die Integer-ID. Der Speed ist wahrlich beeindruckend. 330k Writes pro Sekunde sind hier möglich (im Test wurden 5mio Zeilen in ca. 15 Sekunden geschrieben!!). Auch der lesende Zugriff kann sich sehen lassen, hier werden nahezu die gleichen Werte erreicht. Damit hätten wir auf keinen Fall gerechnet, aber wahrscheinlich ist eine minimale Lösung manchmal einfach die Beste  . Mal schauen, wie sich die eigene Lösung in weiteren Lasttests beweist - vielleicht kommt sie ja bis zum Produktiveinsatz. Von der Geschwindigkeit und Anpassbarkeit her ist die eigene Lösung zumindest bisher die erste Wahl. Auch einen Dump z.B. einmal pro Stunde auf die Platte zu schreiben ist damit einfach möglich, ebenso das Restore aus diesem Dump. Achja, die CPU-Auslastung hält sich erstaunlicherweise ebenfalls in Grenzen. Einzelne Ausschläge an nahezu 100%, sonst eher moderat. Java wurde übrigens jeweils mit -Xmx7168m gestartet, konnte sich also bis zu 7 GByte RAM abzapfen.
Falls ihr Erfahrungen mit NoSQL habt, freuen wir uns über Kommentare. Außerdem möchten wir zu bedenken geben, dass unsere Tests natürlich nicht repräsentativ sind. Nicht immer kommt es nur auf schnelle Writes und Reads an. Diesmal aber eben schon ..
Tuesday, 1. December 2009
Heute mal ein etwas kürzerer Beitrag von mir. Ich stand vor kurzem vor dem Problem die default resource limit Werte meiner Linux-Box auslesen zu müssen, um zu wissen ob ein Programm mit der zu testenden Konfiguration auch wirklich sauber läuft (im speziellen ging es um die Möglichkeit den virtuellen Speicher des Prozesses komplett im Hauptspeicher zu behalten, welches mit mlock() möglich ist, es gibt aber ein resource limit, nämlich RLIMIT_MEMLOCK welches die größe beschränkt). Da ich weder ein Tool, noch sonst irgend etwas gefunden habe, was dies möglich macht, habe ich es einfach selber gecoded  .
Eventuell benötigt einer von euch das ja irgenwann einmal. Das Tool, welches ein quickhack in C ist  , gibt es hier: getrlimits.c
Kompiliert bekommt Ihr das ganze via gcc -o getrlimits getrlimits.c.
Monday, 2. November 2009
Wer mit SQL zu tun hat und viel mit JOINS arbeitet, steht öfter mal vor der Frage, welcher JOIN jetzt für welchen Zweck am sinnvollsten und vor Allem am performantesten ist. Da mir das auch hin und wieder so geht, finde ich das "SQL JOINS Cheatsheet" durchaus hilfreich, denn es stellt grafisch dar, welche Ergebnismengen bei welchen JOINS beachtet werden.
Aber seht selbst:
[ via adminlife.net ]
Tuesday, 23. December 2008
Nachdem sich memcached offenbar immer größerer Beliebtheit erfreut und auch das kürzlich stattgefundene MySql-Webcast " Bereitstellung von Web-2.0-Anwendungen mit MySQL und Memcached" im Anschluss sehr viele User-Fragen hervorrief, habe ich im Adminwiki mal ein kleines Howto geschrieben (Link am Ende des Artikels). Über die Vorteile von memcached gegenüber anderen Caching-Methoden hatte ich ja bereits berichtet.
In dem Howto geht es um den wohl tausendfach vorhandenen Anwendungsfall, dass eine Web-Applikation in PHP mit angeschlossener MySql-Datenbank optimiert werden soll. Die Datenbank bildet bei sehr vielen Zugriffen schnell den Flaschenhals bei derartigen Anwendungen, zumal sich diese Daten nicht so einfach skalieren lassen wie auf einem Webserver. Zumindest ist es i.d.R. einfacher, einen 2. Webserver aufzusetzen oder teile davon auszulagern, als z.B. Replikation oder Clustering in die Datenbank-Struktur einzubauen.
Hier hilft memcached enorm, denn es setzt sich einfach zwischen Anwendung und Datenbank. Die Implementation ist dabei denkbar einfach und eignet sich für so ziemlich jede Script- bzw. Programmiersprache und natürlich auch für jeden Datenbankserver. Im Beispiel ist es einfach PHP mit MySql (LAMP halt), da ich ähnliche Konstellationen bereits mehrfach im Einsatz habe.
Das Howto gibts drüben im Adminwiki - Kommentare sind natürlich erwünscht!
PS: Auch wenn ich kein allzu großer Fan von Weihnachten bin, wünsche ich allen Lesern ein frohes Fest
Thursday, 11. December 2008
Das Parsen von HTML-Dokumenten gehört nicht unbedingt zu meinen Lieblingsaufgaben, was sicherlich zum Teil auch in meiner begrenzten Erfahrung mit regulären Ausdrücken begründet ist. Sagen wir so: Reguläre Ausdrücke sind für mich das, was für den Rob CSS sind  .
Gott sei Dank hat ein findiger Japaner für RegEx-n00bs mit Perl-Hintergrund wie mich dieses Modul entwickelt. Damit bin ich schnell und komfortabel zum Ziel gekommen.
use Web::Scraper;
# neue Scraper-Instanz anlegen und die Parse-Regel definieren
my $s = scraper {
process '//li[@class="blau"]/h1/a', 'texte[]' => '@href';
};
# html nach regel parsen
my $r = $s->scrape($html);
my $result = $r->{'texte'};
Im Beispiel wird aus allen Links, die sich in einem h1-Element befinden, welches sich wiederum in einem li-Element der Klasse "blau" befindet, der Wert des Attributs "href" herausgezogen. Im Result befinden sich dann eine Referenz auf ein Array, welches die Werte enthält. Die Regel zum Parsen ist übrigens mittels XPath-Syntax zu definieren.
Obwohl das Modul erst vor einiger Zeit im foo-Magazin vorgestellt wurde, hier noch einmal eine Liebeserklärung von meiner Seite
Monday, 17. December 2007
Bevor ich hier die Welt mit meinen News beglücke, soll ich brav sein und mich vorstellen meint Rob. Also: ich bins - der Tom  . Im Job programmiere ich mit Perl, privat versuche ich (nicht nur) den Rob von der "Power of CSS" zu überzeugen. Mit noch wenig Erfolg  .
Dabei bin ich übrigens über folgend Zeilen gestolpert:
#GeorgeWBush{
position:absolute;
bottom:-6ft;
} Das wäre doch was fürs "globale Stylesheet"
Und wer noch was für den technikbegeisterten Opi zu Weihnachten sucht klickt hier.
seniorenland.com - hier werden die Senioren noch mal richtig übern Tisch gezogen...
In diesem Sinne schon mal
Frohes Fest
Monday, 29. October 2007
Ich hasse Spam - Du doch auch, oder? (Botnetzbetreiber lesen bitte gleich den nächsten Artikel)!
Da ja bekanntlich die bösen Bots (nein, ich meine an dieser Stelle nicht den Googlebot  ) wehrlose Webseiten nach eMail-Adressen durchgrabben, um diese dann für Spammings zu verwenden bzw. zu 100k-Blöcken oder sowas an hochseriöse Firmen für das Taschengeld eines 15-Jährigen zu verticken, sollte man heutzutage seine eMail-Adresse nicht mehr im Klartext oder gar als mailto: - Link auf der eigenen Website platzieren.
Viele Webmaster greifen zu tollen Grafiken, die dann die Mailadresse darstellen - i.d.R. sehen diese aber irgendwie nicht so toll aus und entsprechen nicht dem Design der Homepage. Auch bei Schreibweisen wie "name[at]domain.de", "name(at)domain.de" oder "name.at.domain.de" kann man sich wohl kaum sicher fühlen - schließlich sind das 2 Zeilen Code, die der Botbetreiber ändern muss, um so etwas auslesen zu können. Ein wenig effektiver erscheint mit da noch die Variante "name at domain dot de", denn das könnte ja auch ein normaler Text sein. Dennoch wird das sicherlich auch schon längst erkannt und mit in die Spamdatenbanken aufgenommen.
Deshalb nutze ich seit Jahren eine kleine JavaScript-Funktion mit dem Versuch, das Ganze zu umgehen:
function mail_me() {
pt1 = "robert";
pt2 = "klikics";
tld = "de";
window.location = "mailto:" + pt1 + "@" + pt2 + "." + tld;
}
Idealerweise sollte man den Funktionsnamen und die Variablennamen (pt1, pt2 & tld) immer mal verändern, wenn man es auf verschiedenen Websites einsetzt. Prinzipiell denke ich aber schon, dass die Spambots dies nicht auslesen können. Und die Besucher können den Link im Stile javascript:mail_me() wie gewohnt anklicken.
Mittels PHP ist es ebenfalls möglich, eine eMail zu kodieren:
function mask_email($email) {
$masked_mail = "";
for($i = 0; $i < strlen($email); $i++) {
$char = $email{$i};
$masked_mail.= "".ord($char).";";
}
return $masked_mail;
}
Bei dieser Variante wird die jeweilige ASCII-Kodierung genutzt. Meine eMail-Adresse lautet dann robert@klikics.de.
Natürlich kann kein Mensch, der nicht gerade Obergeek oder sowas ist, diese Kodierung lesen, um eine Mail an den dahinter verborgenen Empfänger abzusetzen. Zum Glück wissen aber die Browser etwas damit anzufangen, weshalb man via mailto:[kodierte Zeichen] problemlos eine Mail an den Empfänger absetzen kann. Natürlich kann ich nicht garantieren, dass die Spambots dies nicht auch schon verstehen, allerdings bestehen daran berechtigte Zweifel ... in diesem Sinne: "Fight for a spamfree world!" - oder so ähnlich
Saturday, 15. September 2007
Ich benutze seit einer ganzen Weile den Loadbalancer pound, da dieser sehr wenige Ressourcen benötigt, selbst wenn die Seite stark frequentiert ist. Außerdem ist es sehr bequem, da dieser auch als SSL-Proxy arbeitet, d.h. man muss die Keys nur auf dem Loadbalancer einspielen und die Backends arbeiten normal mit HTTP auf Port 80 weiter. Alles in Allem eine sehr runde Sache, die ich ruhigen Gewissens weiterempfehlen kann.
Natürlich will ein Statistik-Freak wie ich dann auch wissen, wieviele Requests pro Sekunde der LB abarbeiten muss. Pound bietet zwar mit dem Tool poundctl seit einigen Versionen ein Kommandozeilentool, mit dem man die Vitalität der Backende sowie die Anzahl der aktiven Sessions monitoren kann, die Requests pro Sekunde lassen sich aber nicht ohne Weiteres herausfinden. Also musste eine kleine Scriptlösung her, um die Werte für das Monitoring-Tool cacti aufzubereiten. Wenn man Logging aktiviert hat (pound unterstützt verschiedene Loglevel, u.a. auch Apache-Style), ist das kein Problem. Allerdings wird das Logfile sehr groß, wenn man viele Requests hat. Im Apache-Format schnell mal 10 GByte/Tag - und i.d.R. hat man in einem LB nicht gerade 500er Platten verbaut
Also das Logging ein wenig reduziert (Loglevel 3 in der Config), so dass nur IP und Aufruf in je einer Zeile geloggt werden. Damit halbiert man die Größe des Logfiles ca. um die Hälfte. Mit einem kleinen PHP-Script kann man dann einfach aus der /var/log/daemon.log die Anzahl der Zeilen je Sekunde bzw. Minute herausfinden. Und so sieht das Ganze dann aus:
$tstamp = date("H:i", strtotime("-1 minute"));
$call = "tail -n 200000 /var/log/daemon.log|grep $tstamp|wc -l";
$num = `$call`;
$num = $num / 60;
echo round($num);
Damit werden die Requests pro Minute ermittelt und dann durch 60 geteilt, um den Wert je Sekunde zu bekommen. Wie man das Script dann ganz einfach in cacti einbindet, wird hier beschrieben.
Nach meiner Veröffentlichung dieses kleinen Codefetzens in der pound-Mailingliste hat Bob Apthorpe sich gleich noch hingesetzt und das Ganze in Perl gebaut, so dass man es einfach als Daemon laufen kann. Sein Script samt Doku ist hier zu finden. Beide Lösung geben die gleichen Werte zurück, so dass es eigentlich keine Rolle spielt, welches man verwendet. Ich nutze die PHP-Lösung und frage einfach von meinem Cacti-Server mit einem weiteren Miniscript via ssh loadbalancer php script.php aller 5 Minuten den Wert ab.
Und so sieht das Ganze dann als Graph im Cacti aus:
|
Kommentare