Als Windows User benutze ich seit einiger Zeit msysgit - Git for Windows und dort die portable Version. Das funktioniert auch alles super, bis auf die Tatsache das SSH Keys und die .bash_history im Home Verzeichnis des lokalen Rechners/Users gespeichert werden und man somit nicht wirklich portable ist. Das kann man aber sehr einfach ändern und so gehts.

Man erstellt sich einen Wrapper für die git-bash.bat. Ich hab die Datei start-git.bat genannt und sie in den gleichen Ordner gepackt. In der Datei passt man nun den Pfad zum Home Verzeichnis an.

@echo off
rem Copyright (C): 2010 Voyagerfan5761
rem http://technobabbl.es/

set USERPROFILE=%~dp0%home\
set HOMEDRIVE=%~d0%home\
set HOMEPATH=%~p0%home\
set HOME=%~dp0%home\

git-bash.bat

%~dp0% ist der Pfad zum Wrapper und dort habe ich noch das Unterverzeichnis home angelegt. Startet man git-bash nun über den Wrapper, landen in diesem Verzeichnis die SSH Keys und die .bash_history und man kann sie auch auf einem anderen Rechner benutzen. Diese Lösung habe ich in diesem Post gefunden.

Ich hinke zwar Jahre hinterher, aber seit ein paar Monaten benutze auch ich memcached zum Cachen von Webinhalten. Memcached ist ein Memory Speicher der Inhalte komplett im Arbeitsspeicher des Servers vorhält und deshalb sehr schnell ist. Das heißt aber auch das nach einem Server Restart oder Neustart des memcached alle Daten aus dem Cache gelöscht sind. Man kann SQL Results speichern, das Session Management darüber abwickeln oder andere Bottleneck Zugriffe cachen. Hier habe ich schon einmal erklärt wie man memcached auf einem Debian System installiert und in diesem Artikel möchte ich auf den Zugriff aus PHP heraus eingehen.

PHP Memcached oder PHP Memcache?

Hat man den memcached installiert, möchte man ihn natürlich auch benutzen. Ein Blick ins PHP Manual zeigt dann aber gleich 2 Lösungswege um aus PHP auf den memcache Deamon zu greifen zu können. Welche ist aber die richtige? Beide sind PECL Extensions, also nicht direkt bei PHP dabei.

PHP Memcache

PHP Memcached

Den aktuellen Stand und die Unterschiede zwischen den Versionen hat Brian Moon in seinem Blog Post PHP and Memcached: The state of things erklärt. Ich habe mich deshalb für die zwar ältere aber stabilere memcache Version entschieden.

$ aptitude install php5-memcache

Zugriff auf den memcached per PHP

Hat man memcache und memcached installiert, möchte man natürlich auch darauf zugreifen. Das macht man am besten über eine eigene wrapper Klasse um einen zentralen Punkt für Anpassungen und Erweiterungen zu haben. Wer keine eigene schreiben möchte, kann meine PHP5 singleton Wrapper Klasse benutzen Download. Sie unterstützt nur die Standardfunktionen set, get, delete, sowie connect und close.

require_once('class.kmemcache.php');

//speichert $wert mit dem $name
//der Wert soll nach 11 Sekunden expiren
kmemcache::set($name, $wert, 11);

//auslesen von $name
$wert = kmemcache::get($name);

//löschen von $name
kmemcache::delete($name);

Die set/get Befehle setzt man nun um seine zu cachenden Abfragen und fertig ist die memcached Einbindung.

require_once('class.kmemcache.php');

$name = 'langermysqlquery';
if( ($result = kmemcache::get($name) ) === FALSE )
{
   //sql query ausführen
   $result = '';

   //result in memcache speichern für 1 Tag
   kmemcache::set($name, $result, 86400);
}

Manchmal spinnt das automatische Update und da ich nicht jedes mal aufs neue im Manual nach der Anleitung für ein manuelles Update suchen möchte, hier kurz die nötigen Schritte aufgelistet. Hier ist der Link zum Manual Eintrag: Update Piwik.

  • auf dem Server einloggen und die config/config.ini.php sichern
  • wget http://piwik.org/latest.zip
  • unzip latest.zip (aptitude install unzip)
  • sicherstellen das alle Dateien im richtigen Ordner angekommen sind.
  • Verzeichnisse aufräumen
  • chmod -R 777 tmp/
  • chmod -R 777 config/
  • Piwik Seite aufrufen und Datenbank Updaten
  • fertig

Fast so einfach wie das automatische Piwik Update.

Eine gut benutzte Piwik Installation wächst sehr schnell und kann zum Problem werden wenn man sich nicht darum kümmert. Bei Shared Webhosting gibt es manchmal maximal Größen für Datenbanken, aber auch bei Dedicated Servern kann es ab einer bestimmten Größe zu Problemen kommen. In Piwik gibt es die Archiv Tabellen (piwik_archive_*), die die Statistiken speichern und dann gibt es 2 große Log Tabellen (piwik_log_action, piwik_log_link_visit_action), aus denen die Statistiken zusammen gebaut werden. Man hat nun 2 Möglichkeiten die Datenbank zu verkleinern. Man kann die Einträge in den Log Tabellen löschen die schon processed wurden und wenn man keine "alten" Daten Griffbereit braucht, kann man auch noch die jeweiligen Archiv Tabellen löschen (vorher exportieren/archivieren).

Für die Log Tabellen einfach das Archive Crontab ausführen oder sicher sein das die Einträge wirklich abgearbeitet wurden. Am besten einmal von Hand aufrufen. Ich war mir sicher das es läuft, aber habe es doch von Hand aufgerufen und siehe da, es brach durch memory exhaustion an einer bestimmten Stelle ab. Nachdem ich das Memory Limit erhöht hatte lief es dann durch. Nun führt man folgenden SQL Befehl aus:

DELETE piwik_log_visit, piwik_log_link_visit_action
FROM piwik_log_visit INNER JOIN piwik_log_link_visit_action
WHERE piwik_log_visit.idvisit = piwik_log_link_visit_action.idvisit
AND visit_first_action_time <= DATE_SUB(CURDATE(), INTERVAL 30 DAY);
OPTIMIZE TABLE piwik_log_visit, piwik_log_link_visit_action;

Ganz am Ende stellt man ein wie viele Einträge man behalten möchte. In diesem Fall wird alles was älter als 30 Tage ist aus den Log Tabellen gelöscht. Kommt direkt aus dem Piwik FAQ: How to purge Piwik Logs?. Wer sich unsicher ist ob das wirklich richtig funktioniert, sollte vor dem ausführen eine Sicherheitskopie der beiden Tabellen oder gleich der ganzen Datenbank anlegen. Nach dem löschen noch ein Optimize Table auf die beiden Log Tabellen ausführen.

Ansonsten habe ich mir angewöhnt mehrere Piwik Installationen laufen zu lassen. Hat eine Webseite sehr viel Traffic, setzt man ein eigenes Piwik für sie auf. Durch das automatische Update ist die Wartung auch von mehreren Piwiks sehr einfach.

Im Mysqlperformanceblog wurde ein sehr interessanter Punkt angesprochen und das ist Overhead bei simplen Checks. Checking for a live database connection considered harmful. Das passiert mir leider auch zu oft das ich erst selten auftretende Fälle ab handle und dann zum 99,99% Fall komme.

Gestern wurde die lang erwartete CakePHP 1.3 als Stable veröffentlicht. Die Release Candidates hatte ich mir nicht wirklich angesehen, aber CakePHP 1.3 soll im Vergleich zu 1.2 schneller laufen, was allein ein Update schon rechtfertigt. Natürlich gibt es wieder eine ganze Menge neuer Features und die Migration Anleitung von CakePHP 1.2 sieht auch nicht besonders kompliziert aus. Mal sehen wann ich die Zeit finde das erste Projekt umzustellen.

*Update*
Ich habe gerade die ersten Projekte umgestellt. Die Migration war wirklich einfach und so habe ich es recht schnell online geschoben um ein paar Benchmarks laufen zu lassen. Leider ist die Performance so was von schlecht, das ich es ganz schnell wieder umgestellt habe... Das muss ich mir erst genauer ansehen und wenn nötig von CakePHP verabschieden. Die 1.2.6 hat schon keine berauschenden Werte geliefert, aber die 1.3.0 lag noch einmal gut 90% drunter.

Related Links

An Funktionen wie file() oder file_get_contents() habe ich mich mittlerweile so gewöhnt das ich sie überall benutze. Das funktioniert auch sehr gut wenn man nur kleine Dateien laden möchte, aber bei größeren Files kommen sie an ihre Grenzen. Das Problem ist das die Datei mit einem mal komplett in den Arbeitsspeicher geladen wird und dieser natürlich beschränkt ist. Bisher habe ich dann einfach das Memory Limit erhöht und gut war, aber bei einem 37GB Logfile ging das nicht mehr. PHP bietet dafür aber Abhilfe durch den Einsatz von Streams. Die Datei wird mit fopen zum lesen geöffnet und dann Zeile für Zeile eingelesen und bearbeitet.

So wurde aus diesem Code

$content = trim(file_get_contents($file));
$lines = explode("\n", $content);
foreach($lines AS $line)
{
	echo $line . "\n";
}

dieser hier.

$fp = fopen($file, 'r');
if($fp)
{
    while(!feof($fp))
    {
    	$line = fgets($fp);
    	echo $line . "\n";
    }
}

Das Memory Limit konnte damit auf 8MB bleiben und das Script verrichtet seine Arbeit. Neben fgets() gibt es auch noch stream_get_line() die das gleiche leistet. Der Unterschied ist das man bei stream_get_line() den Line Separator angeben kann. Schaut man sich die Kommentare auf php.net zu den beiden Befehlen an, dann scheint mal der eine, mal der andere schneller zu sein. Hier sind meine Beobachtungen dazu:

# stream_get_line()
$ time yes "This is a test line" | head -1000000 | \
php -r '$fp=fopen("php://stdin","r"); \
while($line=stream_get_line($fp,65535,"\n")) { 1; } fclose($fp);'

real    0m0.611s
user    0m0.660s
sys     0m0.084s
# fgets()
$ time yes "This is a test line" | head -1000000 | \
php -r '$fp=fopen("php://stdin","r"); \
while($line=fgets($fp,65535)) { 1; } fclose($fp);'

real    0m12.357s
user    0m4.468s
sys     0m0.288s

Scheinbar ist mein System ein stream_get_line() System. :)

Der PHP Gangsta beschreibt in seinem Blog wie man mit PHP Socket Stream Server erstellt und abfragt. Außerdem wird auf die Verwendung von Streamfiltern eingegangen. Damit ist es zum Beispiel möglich die Übertragenen Daten per gzip oder bzip2 zu komprimieren.

Witziger weise habe ich gerade zu einem ähnlichen Thema recherchiert und 5 Minuten später spuckte der RSS Feed diesen Post aus. :)

Related Links

Einer der großen Vorteile von Piwik zu Google Analytics sind die Realtime Statistiken. Will man als Standard Einstellung nun nicht Gestern haben, sondern Heute, kann man dies in der config.ini.php einstellen. Einfach unter General das folgende eintragen:

[General]
default_day = "today"

Öffnet man nun Piwik, wird immer der aktuelle Tag angezeigt.

Ein üblicher Prozess beim Programmieren mit Relationalen Datenbanken (in diesem Fall MySQL) und besonders im Web ist das „wenn vorhanden, update, wenn nicht vorhanden insert“ Konstrukt was man in vielen Anwendungen sieht. Auch in meinen Scripten gibt es diese Abfragen und es hat mich schon immer gestört, da man recht viel Code für eine simple Sache braucht. Glücklicherweise gibt es Abhilfe zu diesem Problem, wenn man Primär oder Unique Keys benutzt einfügen/updaten will.
Continue reading »