Amazon S3: Wie wir einen Image-Server realisiert haben

“Cloud” ist in aller Munde. Amazon S3 (Amazon Simple Storage Service) ist im Prinzip eine Cloud. Amazon selbst beschreibt diesen Dienst als “Speicher für das Intranet”. Ich kann also schnell und einfach Dateien dort ablegen und von überall zugreifen. Die Daten werden gespiegelt dort abgelegt, die Datenanbindung ist vorbildlich. Soweit so gut. Nichts wirklich spannendes. Clouds gibt es inzwischen wie Sand am Meer und die meisten Nerds können sich bei der Vorstellung eines neuen Cloud-Anbieters das Gähnen nicht verkneifen. Auch Amazons „Ausführungsanforderungen“ lesen sich wie klassisches Bullshit-Bingo: „Sicher“, „Zuverlässig“, „Skalierbar“, „Geschwindigkeit“, „Günstiger Preis“ und Einfachheit“. Und trotzdem möchte ich den Dienst samt Zugriff per C# vorstellen.

amazon s3 kontoübersichtIm vorliegenden Projekt hatten wir das Problem, eine große Shopping-Seite zu beschleunigen. Alleine die Bilddateien belegten über 50 GB und wurden rege von den Kunden aufgerufen. Schon recht früh erkannte man, dass die Bilder auf einen separaten Server ausgelagert werden mussten, um den Zugriff auf den eigentlichen Webserver zu entlasten. Doch man erkannte, dass ein Server, der die Bilder hostet, nicht mehr ausreicht. Zu groß war die Last, die nur das Ausliefern der Bilder verursachte. Des Weiteren waren die Bilder nicht gespiegelt. Bei einem Servercrash wären die Bilder zwar nicht unwiederbringlich verloren gewesen, doch der Ausfall hätte einiges an Zeit benötigt. Und ein Shopping-Portal ohne Produkt-Bilder ist ein Portal ohne Umsätze. Also überlegte man, wie der/die zukünftigen Server beschaffen sein mussten, damit der Zugriff auf die Bilder schnell erfolgte und diese gleichzeitig zuverlässig gespeichert wurden.
Um alle Anforderungen zu erfüllen mussten also mindestens zwei Server angeschafft werden. Neben den Hardwarekosten, den laufenden Betriebskosten sind die Server-Administrationskosten nicht zu unterschätzen. Dem gegenüber wurden die Kosten für die Amazon S3 gestellt. Für Neueinsteiger bietet Amazon ein AWS Gratisangebot an, was für erste Tests durchaus akzeptabel ist. Dies genutzt, konnte der Dienst ein paar Wochen gestestet werden. Zugriffe und deren Geschwindigkeit wurden getestet, die (produktiv anfallenden) Kosten der Eigenlösung gegenübergestellt.
Aktuell halte ich die Amazon Webserver-Angebote nicht unbedingt für finanziell Vorteilhaft (einige Spezial-Angebote ausgenommen). Auch kleinere Webseiten sind sicherlich besser damit bedient, Teile ihres Webs auf eine andere virtuelle Maschine bei einem Hosten auszulagern, als die Daten in einer S3 zu speichern, denn bei jedem Zugriff eines Users tickt der Amazon-Zähler. Und da kann schon ein „Sümmchen“ zusammenkommen.
Doch bei dem oben beschriebenen Projekt ergab sich ein Kostenvorteil. Zudem überzeugte die Zugriffszeit der S3. Genutzt wird der Standort EU (Irland).

Weiter: Amazon S3: Zugriff auf S3 per C#

Suchmaschinen-Besucher und Search-Keys aus Apache-Logfile ermitteln

Es interessiert einem brennend: Wie viel Besucher habe ich durch Suchmaschinen erhalten und vor allem, nach was haben die Besucher über die Suchmaschine gesucht? Die Antwort gibt das Server-Logfile, welches detailliert die Besucher protokolliert. Die gängigste Methode ist, das Logfile in einem Texteditor zu öffnen und manuell nach den Zugriffen zu schauen. Dies macht man eine Weile, dann hat man keine Lust mehr. Denn man möchte schnell eine Übersicht über die Suchmaschinen-Besucher und ihre Suchbegriffe.
Dann sucht man im Internet nach Logfile-Parser und wird schnell fündig. Neben online-Webstatistiken wie AWStats oder Freeware für das Offline-Parsen findet man eine ganze Reihe an Parsern; die meisten jedoch viel zu kompliziert oder eben nicht speziell für diesen Zweck zugeschnitten, so dass die Handhabung zu umständlich ist.

Logfile-Parser speziell für das Ermitteln von Suchmaschinen-Benutzer
Irgendwann war ich es leid und habe den Sirmark-Suchmaschinen-Pareser geschrieben, den ich hier zum Download anbiete. Das Programm ist Freeware, Ihr könnt es kostenlos downloaden und benutzen. Einen Support biete ich jedoch NICHT!

DOWNLOAD: Logfile-Parser SLP V1.2

Systemvoraussetzungen für den Suchmaschinen-Parser
Der Parser ist für Windows .NET geschrieben. Er läuft ab Windows XP und benötigt mindestens das .NET-Framewerk 2.0. Er besteht aus einer einfachen EXE, benötigt keine Installation oder Registry-Einträge. Also einfach downloaden und starten.

Benutzung von Sirmarks-Suchmaschinen-Parser

Apache Logfile auswerten
Logfile Parser: Apache Logfile öffnen

Die Handhabung des Suchmaschinen-Parsers ist sehr einfach. Nach dem Starten der Anwendung klickt man auf den Button „Logfile laden“. Es öffnet sich ein Dialog, über den man das Logfile auswählt. Dies bedeutet, dass das Logfile also auf der lokalen Windows-Maschine oder per Datenfreigabe über Windows erreichbar sein muss. Also gegebenenfalls das Logfile via FTP vom Webserver herunterladen.
Gezippte Logfiles: Der Logfile-Parser entpackt das Logfile NICHT automatisch. Wenn es sich um ein gezipptes Logfile handelt („.gz“), das Logfile vorher beispielsweise mit 7.zip entpacken!

Logfile nach Suchbegriffen analysieren

Logfile nach Suchbegriffen analysieren
Alle Besucher von Suchmaschinen werden mit dem Suchbegriff ermittelt

Wenn ein Logfile ausgewählt wurde, beginnt der Parser sofort mit seiner Arbeit. Er geht das Logfile zeilenweise durch und zeigt die gefundenen Zeilen in der Box „Zeilen“ zur Laufzeit an. Die Anzahl der Zeilen sowie der Fundstellen wird in einem Label angezeigt.
Wenn das Logfile komplett analysiert wurde, werden die Suchstellen aufbereitet. In der unteren Tabelle erscheinen alle Suchstellen aufbereitet nach Datum, Suchmaschine, Search-Key, Ziel-URL und die IP-Adresse des Besuchers.

Tipps für die Nutzung des Suchmaschinen-Parsers
Beide Tabellen sind durch einen horizontalen Slider miteinander verbunden. Wenn man mit der Maus in den Bereich zwischen der Anzeige „Zeilen“ und der Tabelle fährt, ändert sich der Mauszeiger und man kann die Größe der Elemente ändern.
Die Datenzeilen in der aufbereiteten Tabelle sind markierbar und können so per Copy and Paste schnell in eine Textdatei oder Excel kopiert werden. Um beispielsweise die gesamte aufbereitete Tabelle nach Excel zu kopieren, klickt man am einfachsten (1) in der Tabelle an; die gesamte Tabelle wird markiert. Nun wählt man auf der Tatstatur „Strg“ + „c“ für „Copy“, wechselt dann nach Excel oder einem Texteditor und wählt dort einfügen (oder „Strg“+ „p“ („Paste“).

Change-Log
Version 1.1:

  1. Suchanfragen werden nun per URL Decode umgeschlüsselt.
  2. BUG in Suchanfrage (String-Länge) bereinigt.
  3. Bot, Spider und Crawler-Tab hinzugefügt: Ab dieser Version werden auch alle Crawler-Zugriffe aus dem Logfile extrahiert und aufbereitet in einem eigenen Tab aufgelistet. Durch die Möglichkeit, die Tabelle zu sortieren (Klick in den Tabellen-Spaltenkopf), kann schnell eine Übersicht über die Crawler-Tätigkeit einzelner Bots (Googleboot, Bing etc) erhalten werden.

Version 1.2

  1. Tool kommt nun mit weiteren verschiedenen Apache-Logfiles zurecht (Logfile Aufbau-Erkennung)
  2. Beschleunigung der Verarbeitungsgeschwindigkeit: Durch C#-Optimierung konnte die Abarbeitungsdauer eines Referenz-Logfiles von 1:37 min auf 1:13 min minimiert werden.
  3. Ausführungsdauer wird nun im Fenstertitel mit angezeigt
  4. Um die Verarbeitungsgeschwindigkeit zu beeinflussen, kann nun der Analysemodus (alles, nur Suchanfragen, nur Bots) beeinflusst werden
  5. BUG: Aufgrund der optimierten Abfragegeschwindigkeit kann es vorkommen, dass normale Besuchereinträge fälschlicherweise als Bot erkannt werden.

Alternativer PHP Cache (APC) installieren

Installation Alternativer PHP Cache APC Linux Ubuntu Plesk

Die Installation des „Alternativer PHP Cache“ (APC)  ist eigentlich schnell erledigt. Da jedoch nicht jede im Internet verfügbare Lösung oder jedes HowTo zum Ziel führt, möchte ich hier einige Wege aufzeigen. Gerade unter Ubuntu/Plesk kann die Installation zu Problemen führen. Wie diese Probleme umgangen werden, zeige ich hier.

APC bzw. der Alterative PHP Cache ist ein freies PHP Modul, welches eine robuste Möglichkeit bietet, PHP Zwischencode zu cachen und somit die Zugriffszeiten einer dynamischen PHP-Seite massiv zu optimieren. Gerade Google scheint immer mehr darauf zu achten, dass eine Webseite möglichst schnell geladen wird. Das Stichwort hierzu lautet Page Speed.  Da offensichtlich die Ladezeit einer Webseite sich auch auf den Google-Suchindex auswirken, müssen Webmaster inzwischen auch die Ladezeit im Auge behalten. Im Prinzip auch ein richtiger Ansatz. Kaum ein Leser möchte bei jedem Klick mehrere Sekunden auf den Aufbau der folgenden Seite warten. Im Zeitalter von Breitband-Anschlüssen und DSL verliert der Leser bei langen Wartezeiten schnell das Interesse an einer Seite.

Über die PHP.ini ist schnell ersichtlich, ob APC installiert wurde

Das PHP-Modul APC ist aktuell noch nicht im Basis-Umfang von PHP enthalten. Dies wird wohl – wie man im Internet lesen kann – sich in einer der folgenden Versionen ändern. Wohl aus diesem Grund ist das Interesse an diesem Cache sprunghaft gestiegen, ist es doch ein Garant, dass der Code dieses Caches von den Machern von PHP als valide angesehen wird. Aktuell müssen wir APC noch per Hand nachinstallieren. Prüfen Sie zuerst über eine PHPINFO(), ob das Modul APC auf Ihrem Server installiert ist.

Je nach Linux-Webserver-Installation führen unterschiedliche Wege zur Installation von APC Bytecode Cache. Im einfachsten Fall genügen die folgenden Zeilen:

# sudo aptitude install php-apc
# sudo /etc/init.d/apache2 restart

Leider wird gerade unter Plesk der Versuch wie folgt enden:

root@XXX:/usr/local/src/APC-3.1.8# apt-get install php-apc
Reading package lists... Done
Building dependency tree
Reading state information... Done
E: Couldn't find package php-apc

Wenn APC nicht als Paket vorhanden ist, müssen wir es manuell installieren. Zuerst besuchen wir die Seite http://pecl.php.net/package/APC und ermitteln die aktuellste Version sowie den Download-Link der aktuellen TGZ-Version. Hier in diesem Beispiel ist es die Version „APC-3.1.9.tgz”.

cd /usr/local/src
wget http://pecl.php.net/get/APC-3.1.9.tgz
tar xf APC-3.1.9.tgz
cd APC-3.1.9/
phpize

Fehlermöglichkeit PHP: pecl install liefert „ERROR: ‚phpize‘ failed“
Wenn nach der Eingabe von „phpize“ die Fehlermeldung „ERROR: ‚phpize‘ failed“ erscheint, deutet dies darauf hin, das PECL nicht auf dem Server installiert wurde. Holen Sie dies nach:

apt-get install apache2-threaded-dev php5-dev php-pear make

Wenn der Befehl “phpize” keinen Fehler mehr liefert, können Sie wie folgt weitermachen:

./configure
make
make install

Nun ist APC erfolgreich installiert. Damit PHP dieses Modul auch läd und nutzen kann, muss es in der php.ini auch eingefügt werden. Hierzu können Sie folgenden Befehl nutzen:

echo „extension=apc.so“ >>/etc/php.ini

Status des APC-Cache

Je nach Installation hat PHP auch einen Ordner mit dynamischen Inhalten. Prüfen Sie, ob der Ordner
/etc/php5/apache2/conf.d
vorhanden ist. Wenn ja, kopieren Sie ein vorhandenes Ini-File und ändern den darin vorhandenen Dateipfad wie folgt ab:
extension=apc.so

Nach dieser Änderung müssen Sie den Apache Webserver neu starten:
/etc/init.d/apache2 restart
Plesk-Nutzer sollten den Neustart des Apache Webserver über die Weboberfläche von Plesk erledigen.

Prüfen Sie über eine PHPINFO(), ob PHP APC erfolgreich läd.

Daten unter Linux zippen und schnell übertragen

Wenn man beispielsweise eine MySQL-Tabelle von einem Master Server zum Slave übertragen muss und diese Tabelle schon eine stattliche Größe angenommen hat, macht man sich durchaus Gedanken, wie man den Datenübertrag am sinnvollsten bewerkstelligt.

Datenübertrag per WinSCP
Wer sich zwischen der Windows und der Linux-Welt bewegt, kennst sicherlich das Windows-Tool WinSCP. Mit diesem Tool kann von einem Windows-Rechner per grafischer Benutzeroberfläche auf einen Linux-Server zugegriffen werden. Auch der Datenaustausch zwischen beiden Rechnern ist möglich. Wenn allerdings von einem Linux-Recher zum Anderen die Verbindung erfolgen soll, müsste man die Daten zuerst auf Windows-Rechner zwischengespeichert werden, was in den meisten Fällen weniger sinnvoll ist. Positiv anzumerken ist, dass WinSCP auch ein abgebrochener Upload selbstständig wieder aufnimmt.

Datenaustausch mittels SCP (Secure Copy)
Der direkte Weg zur Datenübertragung zwischen zwei Linux-Rechnern sollte per SCP (Secure Copy) erfolgen. Die Übertragung erfolgt verschlüsselt vom Quell- zum Zielserver. Beginnend vom Quellserver gilt folgende Befehlskette:

scp -r orig_directory USERNAME@machine2:destination_directory

Der Parameter “-r” veranlasst SCP das angegebene Directory rekursiv zu übertragen. Wenn man nur eine einzelne Datei übertragen möchte, kann dieser Parameter weggelassen werden.

Nach Absetzen des Befehles nimmt SCP Kontakt zum Zielserver auf. Danach erfolgt die Abfrage des Passwortes (von USERNAME) und der Übertrag beginnt.
NACHTEIL: Leider kann ein abgebrochener Upload mittels SCP nicht wieder aufgenommen werden. Da gemäß Murphys Gesetz der stundenlange Upload meist bei 99 Prozent abbricht, ist dies ein sehr ärgerlicher Nachteil. Wie man den Abbruch eines Uploads umgehen kann, kann im Artikel „Upload mit SCP abgebrochen, was nun?“ nachgelesen werden.

Dateien unter Linux zippen (komprimieren)
Die bekannteste Komprimierungsform unter Windows ist sicherlich ZIP. Auch unter Linux ist ZIP erhältlich, wenn auch nicht in jeder Distribution vorhanden. In den Linux-Paketen ist der Zipper jedoch meist integriert, so dass die Installation schnell und einfach erledigt ist.

Um ein Directory zu zippen, genügt folgender Befehl:

zip -r archive_name.zip directory_to_compress

Um eine Datei zu entpacken, genügt:

unzip archive_name.zip

Dateien packen mittels TAR.GZ
Verbreiteter als der Zipper ist in der Linux-Welt sicherlich tar.gz. Glaubt man einigen Meinungen im Internet (die ich nicht in Frage stelle), erreicht dieser Zipper wesentlich bessere Komprimierungsraten und schon die CPU beim zippen im Gegensatz zum Zipper.
Um ein Verzeichnis zu zippen, wählt man:
tar -zcvf archive_name.tar.gz directory_to_compress

Zum Entpacken im aktuelle Verzeichnis wählt man
tar -zxvf archive_name.tar.gz

Will man seine Daten in einem bestimmten Verzeichnis entpacken, gibt man das Zielverzeichnis explizit an:
tar -zxvf archive_name.tar.gz -C /tmp/extract_here/

Upload mit SCP abgebrochen, was nun?

Da ich aktuell meine Server-Cluster mit den Replikations-Datenbanken bestücke, schaufel ich gigabyteweise Daten durch das Netz. Die Dateien sind gepackt bzw. gezippt größer als zwei Gigabyte, also ein nennenswerter Brocken, bei einer normalen DSL-Leitung, deren magerer Upstream an alte ISDN-Zeiten erinnert. Umso ärgerlicher, wenn der Upload der Datei abbricht, denn wie es das Schicksal will, bricht der Upload nicht nach wenigen Minuten, sondern kurz vor Schluss, nach Stunden, bei 97, 98 oder 99 Prozent ab. Es ist nicht leicht, hier ruhig vor der Konsole sitzen zu bleiben.

Bisher habe ich das Replikationsverzeichnis gezippt und dann per SCP (LINK) zum Zielserver geschickt. Leider gibt es keine Möglichkeit, einen abgebrochenen Upload per SCP wieder aufzunehmen. Abhilfe schafft der Befehl rsync. Mit diesem Befehl kann man den abgebrochenen Upload einer Datei „reparieren“. Der Syntax des Befehls lautet wie folgt:

rsync -e ssh –partial –progress quelle [benutzer@]zielrechner:zielverzeichnis/

RSync: Daten und Datenverzeichnisse unter Linux synchronisieren
RSync ist eigentlich dafür gedacht, Daten und ganze Datenverzeichnisse über ein Netzwerk zu synchronisieren. RSync prüft hierzu die Unterschiede zwischen dem Quell- und dem Zielrechner und überträgt nur die Änderungen. Dadurch, dass Rsync auch einzelne Dateien in Teilen ersetzen kann, eignet sich das Programm auch zur Wiederaufnahme abgebrochener Transfers.

Was bedeuten die Parameter von rsync?
-e ssh: Diesen Parameter sollte man aus Sicherheitsgründen beim Transfer über das Internet immer verwenden, da so eine Verschlüsselung der übertragenen Daten erreicht wird.

partial: Der Parameter „partial“ verhindert bei einem Abbruch, dass Rsync die bereits übertragenen Daten nicht einfach löscht.

Progress: Dieser Parameter ermöglicht es, den Fortschritt des Downloads zu überwachen. Für eine ausführliche Darstellung der Synchronisation kann der Parameter „-v“ hinzugezogen werden.

-P: Die Parameter Partial und Progress können mit dem Parameter „P“ zusammengefasst werden.

Komprimierung einschalten
Wenn man nicht gerade eine zip-Datei überträgt, bietet sich eine Komprimierung der übertragenen Daten an. Hierfür eignet sich der Parameter „-z“.

Eine detaillierte Beschreibung der Parameter von Rsync kann auf der Ubuntu-Seite nachgelesen werden.

rsync -Pvze ssh /home/benutzer benutzer@example.com:/backups

Trockentest: Übertragung von Daten nur Testen
Da rsync je nach Konfiguration auch Daten löschen kann, ist ein Datenverlust bei falscher Parameterübergabe nicht ausgeschlossen. Hier bietet es sich an, zuerst einen Test („dry run“) durchzuführen um zu schauen, ob die Parameter richtig gesetzt sind. Eine Simulation kann mit dem Parameter „-n“ gestartet werden.

Excel-Tabelle lesen: Auslesen einer Excel-Tabelle mit C#

Während CSV-Dateien quasi zum Standard für den Datenaustausch definierter Listen erklärt haben, sind Excel-Tabellen für den Datenaustausch eher das letzte Mittel von Privatanwendern. Zu Recht, denn zu groß ist die Gefahr, dass die verwendete Version von Excel nicht kompatibel mit der Version von Excel ist, die der Anwender nutzt. Und kann ich überhaupt voraussetzen, dass der Empfänger zahlender Kunde von Microsoft ist und Excel nutzt?
Trotz dieser Bedenken stößt man immer wieder, wenn auch selten, auf Excel-Listen, die Firmen zum Datenaustausch bereitstellen. Und da wir alle Freunde von Automationen sind, möchten wir natürlich auch automatisiert auf die Excel-Tabellen zugreifen.

Selbstredend kann C# den Zugriff auf Excel-Tabellen gewähren, schließlich nutzen wir das .NET-Framework und es ist nur eine Frage, wie man dies am schnellsten erledigt. Zuerst binden wir in unserem Visual Studio-Projekt unter VERWEISE/COM die „Microsoft Excel 11.0 Object Library“ (oder eine andere Version) ein. Als Using-Direktive nutzen wir „using Excel = Microsoft.Office.Interop.Excel;“. Schon haben wir das nötige Rüstzeug, um automatisiert auf die Excel-Tabelle zuzugreifen.

Wie wir im folgenden Code stehen können, können wir durch die Interop.Excell.dll relativ schnell Zugriff auf die Tabelle erhalten. Der Zugriff auf die Tabelle erfolgt mittels „xlApp.Workbooks.Open“. Es ist von Vorteil, zuvor das vorhandensein der Excel-Tabelle mittels „if (File.Exists(sFile))“ zu prüfen.
Neben einigen Definitionen wird der Code erst mit der Zeile „xlWorkSheet = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);“ interessant. Wir erhalten hier Zugriff auf das erste Excel-Tabellenblatt. Dann ermitteln wir die Größe/Range der Tabelle und gehen die Zeilen mittels for-Schleife durch. Um eine Zelle in einer bestimmten Spalte auszulesen, nutzen wir einfach „string sZelleSpalte1 = (string)(range.Cells[rCnt, 1] as Excel.Range).Value2;“

 

static private void ReadExcel(string sFile)
{
try
       {
       	Excel.Application xlApp;
        Excel.Workbook xlWorkBook;
        Excel.Worksheet xlWorkSheet;
        Excel.Range range;

        int rCnt = 0;
        xlApp = new Excel.ApplicationClass();
        xlWorkBook = xlApp.Workbooks.Open(sFile, 0, true, 5, "", "", true, Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "t", false, false, 0, true, 1, 0);
        xlWorkSheet = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);
        range = xlWorkSheet.UsedRange;

        //Gehe das ganze Zabellenblatt durch
        for (rCnt = 1; rCnt <= range.Rows.Count; rCnt++)
        {
            //Hier haben wir Zugriff auf jede Zeile
            if ((range.Cells[rCnt, 1] as Excel.Range).Value2 != null)
            {
                try
                {
                   string sZelleSpalte1 = (string)(range.Cells[rCnt, 1] as Excel.Range).Value2;
                   string sZelleSpalte2 = (string)(range.Cells[rCnt, 2] as Excel.Range).Value2;
                  }
                 catch { }
              }
         }

          xlWorkBook.Close(true, null, null);
          xlApp.Quit();
      }
      catch (Exception ex)
      {
         Console.WriteLine("Fehler in ReadExcel: " + ex.Message);
       }
}

MySQL Tabellen-Optimierung: Der optimale Datentyp

Hand aufs Herz: Wieviel Zeit invenstieren Sie vor dem Anlegen der ersten Datenbank-Tabelle in das optimale Tabellendesign? Ich hoffe viel, denn durch das richtige Design, die richtig gewählten Dateitypen und letztlich die richtigen Indizes lassen sich die Größe einer Datenbank minimieren und die Abfragen massiv beschleunigen.

Doch meistens ist das folgende Bild eher an der Tagesordnung. Man legt die erste Tabelle an, füllt die mit Testdaten und programmiert die Anwendung. Es kommen Spalten hinzu, manche werden gelöscht, umbenannt und viele schlichtweg vergessen. Später scheut man sich, das Datenbankdesign noch einmal zu überarbeiten, da man froh ist, dass die Anwendung läuft und man keine Zeit mehr investieren möchte.

Doch wenn die Größe der Datenbanktabelle wächst, wird man von der Realität eingeholt. Es rächt sich mit steigender Datenbankgröße, wenn man das Design vernachlässigt hat. Wer nicht ständig die programminternen Datenbankabfragen auf das Vorhandensein eines optimalen Index prüft, wird schnell Schiffbruch erleiden. Und wer zu großzügig mit Datentypen umgeht, wir schnell unnötigen Plattenplatz belegen und seine Datenbank aufgrund der Größe der Tabellen verlangsamen.

Gerade bei den numerischen Datentypen ist es unumgänglich, den „besten“ Datentyp für ein Feld zu wählen. Es gilt immer: Man wählen den kleinsten Datentyp, der für das Feld ausreicht. Wählt man einen zu großen Datentyp, belegt dieser nur unnötiger Weise Speicherplatz. Bremst unter Umständen sogar die Datenbank. Daher ist es wichtig zu wissen, welche maximale Werte ein numerischer Datentyp unter MySQL annehmen kann.

Unten aufgeführt habe ich die gängigen Datentypen (nicht vollständig!) aufgeführt und gebe Tipps für die Verwendung. Eine detaillierte Aufstellung kann in der MySQL-Doku nachgelesen werden.

  • TINYINT: Nimmt Werte zwischen 0 bis255 oder -128 bis 127 auf. Optimal für die ID von kleinen Tabellen (maximal 255 Einträge) oder Status-Felder.
  • SMALLINT: Nimmt Werte zwischen 0 bis 65.535 oder -32.768 bis 32.767 auf. Langt bereits als Datentyp für die ID-Spalte vieler Tabellen.
  • MEDIUMINT: Nimmt Werte zwischen 0 bis 16.777.215 oder -8.388.608 bis 8.838.607 auf. Optimaler Wert für ID-Felder im „kleinen“ Millionenbereich.
  • INT: Nimmt Werte zwischen 0 und 4.294.967.295 oder -2.147.438.648 und 2.147.483.647 auf. Im Gegensatz zu anderen Datenbanken ist der Integer bei MySQL schon sehr, sehr mächtig. Für viele Felder ist dieser bereits schon überdimensioniert und sollte durch einen kleineren Datentyp ersetzt werden.
  • BIGINT: Nimmt Werte zwischen 0 und 18.446.744.073.709.551.615 oder -9.223.372.036.854.775.808 und 9.223.372.036.854.775.807 auf. Gerade Umsteiger von anderen Datenbanksystemen wählen gerne das „große INT“ für die ID einer stark wachsenden Tabelle. Doch unter MySQL ist der der Integer bereits groß genug für die meisten Tabellen.

Speicherplatzbedarf von nummerischen Daten-Typen
Warum wir nur den kleinsten Datensatztyp wählen sollten, der wirklich benötigt wird, zeigt die folgende Tabelle. In dieser ist der erforderliche Speicherplatz eines Datentyp aufgeführt. Wenn wir beispielsweise für eine Spalte großzügig BIGINT (8 Byte) deklariert haben, aber ein SMALLINT (2 Byte) als Spaltentyp reichen würde, verschenken wir 6 Bytes. Pro Datensatz, wohlgemerkt! Wer als Datensatz-ID BIGINT gewählt hat und nun den Spaltentyp auf INT reduziert, spart 4 Bytes (50 Prozent) an Speicherplatz, bezogen auf das ID-Feld. Es lohnt sich also, auch laufende Tabellen zu gegebener Zeit auf die Datentypen hin zu prüfen.

  • TINYINT: 1 Byte
  • SMALLINT: 2 Byte
  • MEDIUMINT: 3 Byte
  • INTEGER: 4 Byte
  • BIGINT: 8 Byte

Update: Noch weniger Gedanken macht man sich um TEXT …

Schon mal Gedanken gemacht, wieviel Text in ein MySQL-Text-Feld passt? Viel. Sehr viel, sollte man meinen. Dem ist aber nicht so. Rund 65.000 Zeichen passen in ein Text-Feld. Nachfolgend der Speicherplatz und die maximale Textlänge von MySQL Text Feldern:

TINYTEXT 256 bytes
TEXT 65,535 bytes ~64kb
MEDIUMTEXT  16,777,215 bytes ~16MB
LONGTEXT 4,294,967,295 bytes ~4GB

Praxistest: Auswirkung von Rich Snippets – erstes Fazit

Vor einer Woche haben wir mit dem Praxistest „Auswirkung von Bewertung Rich Snippets auf die Google-Suche“ begonnen. Wir wollen wissen, ob es sinnvoll ist, eine Artikelbewertung in einem Blog einzuführen. Wird Google diese Bewertung in seinen Suchergebnissen anzeigen? Und wird sich eine positive Bewertung eines Artikels auch positiv auf das Suchmaschinenranking auswirken?
Um Antworten auf diese Fragen zu finden, haben wir einen kleinen Test gestartet. In diesem Blog habe ich das WordPress Plugin „GD-Star Rating“ installiert. Einige geneigte Leser nutzen die Bewertungsmöglichkeit bereits, so dass viele Artikel bereits Bewertungen erfahren haben. Zum Start des Testes wurden einige Suchbegriffe festgelegt, die zu Artikeln dieses Blogs führen. Die Position wurde festgehalten. Heute, eine Woche nach Start des Testes ist es an der Zeit, ein Zwischenfazit zu ziehen. In rund vier Wochen möchte ich den Test beenden. Dann können wir abschließend bewerten, ob sich der Rich-Snippet-Spam, den aktuell viele SEO-Webmaster betreiben, auch lohnt.
Ein positiver Effekt der Rich Snippets ist das Steigern der Aufmerksamkeit des Artikels im Suchergebnis. Der Artikel, auch wenn er nicht an prominenter erster Stelle steht, zieht die Aufmerksamkeit dank der gelben Sterne auf sich. Somit stellt sich die Frage, ob diese Artikel auch eine erhöhte Klickrate aufweisen werden. Hier kann nach einer Woche sicherlich noch kein gesichertes Fazit gezogen werden.

Suchanfrage Position Bewertung Erscheint Bewertung CTR Durchschn.Pos
retro hintergrund 5 (4) 1 Bew, 5 (0) ja (nein) 0 (0) 32 (37)
hotmail server 4 (6) 1 Bew, 5 (0) ja (nein) 5% (4%) 5,4 (4,6)
gimp cmyk 6 (8) 1 Bew, 5 (0) ja (nein) 3% (3%) 8,4 (8,3)
lamp server * 1 Bew, 5 (0) ja (nein) 0 (0) 9,2 (9,4)
mysql tabelle kopieren 6 (6) 1 Bew, 5 (0) ja (nein) 5% (7%) 5,7 (5,9)
ubuntu lamp 10 (10) 1 Bew, 5 (0) ja (nein) 6% (5%) 8,6 (7,5)
wordpress artikelbild 4 (5) 1 Bew, 5 (0) ja (nein) 5% (5%) 4,6 (5,1)
datetimepicker 7 (8) 2 Bew, 5 (0) ja (nein) 0 (0) 9,6 (9,5)
mysql collation 6 (6) 1 Bew, 5 (0) ja (nein) 8% (8%) 6,7 (6,8)

Werte der Vorwoche in Klammer
Bewertung: 1 Bew, 5 = 1 Bewertung, 5 Sterne
* Seite 2, Pos 3 (Seite 2, Pos 4)

Werfen wir einen Blick auf die aktuellen Werte, aller ermittelt über eine Google-Suche bzw. über die Auswertung über die Google Webmaster Tools. Das Ergebnis ist erst einmal ernüchternd. Die handverlesenen Suchbegriffe haben nur marginale Veränderungen erfahren. Die meisten Testseiten sind um eine Position nach vorne gerückt, ein paar konnten zwei Plätze gutmachen und zwei Testseiten haben ihre Position behalten. Auffallend ist, dass die Suche nach „retro hintergrund“ sich um eine Position verschlechtert hat.
Da wir für diesen Blog keinerlei Linkbuilding betreiben und auch in der vergangenen Woche wohl auch kein Link von Lesern hinzugekommen ist, handelt es sich bei diesen Positionsveränderungen wahrscheinlich um eine normale Veränderung der Suchergebnisse. Eventuell ist das integrieren der Rich Snippets von Google positiv in das Ranking eingeflossen, was durch die größtenteils positive Rangänderung nahe liegt. Doch hier und heute eine abschließende Aussage zu treffen, dürfte verfrüht sein. Eine weitere Bewertung in vier Wochen dürfte hier aussagekräftigere Ergebnisse liefern.
Schon jetzt kann festgestellt werden, dass Google bei allen Suchbegriffen die Rich Snippet-Werte umgehend in seine Suchergebnisse aufgenommen hat und die Bewertungssterne auch anzeigt. Die Click Through Rate, ermittelt anhand der Google Webmaster Tools, hat sich bei allen Artikel, die auch Plätze im Suchmaschinen-Ranking gutmachen konnten, auch erhöht. Dies liegt allerdings auf der Hand, denn jeder einzelne Platzgewinn im Suchmaschinenranking wirkt sich zwangsläufig positiv auf die Click Through Rate. Somit kann die Steigerung nicht zwangsläufig auf das Einblenden der Ranking-Sterne zurückgeführt werden.

Wir dürfen auf jeden Fall gespannt sein, wie das Ergebnis nach vier Wochen aussehen wird …

Mysql trigger über mehrere datenbanken anlegen

Wir haben uns im Artikel „MySQL: Trigger einrichten – Beispiele zur Verwendung von Triggern“ mit der Anlage und Verwendung von Trigger beschäftigt. Bisher war die Grenze die eigene Datenbank. Also die Quell- und Zieltabelle, die wir mit einem Trigger beehrt haben, lagen innerhalb der eigenen Datenbank. Nun stellt sich die Frage, ob wir mit einem Trigger auch über eine Datenbankgrenze hinweg zugreifen können um Tabellen, die in unterschiedlichen Datenbanken liegen, so mit einem Trigge rzu verknüpfen.

Wir erstellen folgendes Testszenario:
Eine Datenbank „Datenbank1“ mit der Tabelle „testtabelle1“ sowie synchron die „Datenbank2“ mit der „testtabelle2“. Beide „Testtabellen“ bekommen der Einfachheit halber nur ein Feld „id“ mit dem Dateityp „integer“.

Die Tabellenstrukturen lauten wir folgt:
[Datenbank1]

CREATE TABLE IF NOT EXISTS `testtabelle1` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

[Datenbank2]

CREATE TABLE IF NOT EXISTS `testtabelle2` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

mysql insert triggerWir legen nun den Trigger an. Dieser soll von datenbank1.testtabelle seine Werte in datenbank2.testtabelle2 schreiben. Daher wird der Trigger auch in der Quell-Datenbank „datenbank1“ angelegt. Der Einfachheit halber beschränken wir uns in diesem Beispiel nur auf einen INSERT-Trigger:

DROP TRIGGER IF EXISTS `insert_testtabelle2`;
DELIMITER //
CREATE TRIGGER `insert_testtabelle2` AFTER INSERT ON `testtabelle1`
FOR EACH ROW BEGIN
INSERT INTO datenbank2.testtabelle2 (id) VALUES (NEW.id);
END
//
DELIMITER ;

Wie wir sehen, sprechen wir die Ziel-Datenbank „datenbank2“ und die darin enthaltene Tabelle „testtabelle2“ einfach über „datenbank2.testtabelle2“ an.

Nun ist es Zeit, unseren datenbankübergreifenden Trigger einmal auszuprobieren. Wir fügen einen Wert in datenbank1.testtabelle1 ein:

INSERT INTO testtabelle1 (id) VALUES (1);

Wir schreiben also eine “1” in die erste Tabelle. Dieser Wert sollte nun auch in unserer datenbank2.testtabelle2 erscheinen. Wir kontrollieren es:

SELECT * FROM testtabelle2;

mysql insert triggerUnd siehe da, unser Trigger über zwei Datenbanken hinweg hat funktioniert.

Nun stellt sich die Frage, ob wir mit einem Trigger auch Tabellen auf einem anderen Datenbankserver ansprechen können. Nun, leider habe ich hier keine gute Nachrichten zu berichten. Ich habe es bis heute noch nicht geschafft, eine Verbindung zwischen zwei unterschiedlichen Datenbankservern zu erreichen. Synonyme, wie es der Microsoft SQL-Server kennt, sind MySQL leider immer noch fremd.

Datenbankabfrage Join über unterschiedliche Datenbanken
Wie bereits oben gezeigt, funktioniert das Ansprechen einer Datenbank bzw. der darin enthaltenen Tabelle über
Datenbankname.tabelle
Somit ist auch ein Join kein Problem, die richtigen Rechte vorausgesetzt. Wie bei Trigger erwähnt, habe ich bisher noch keine Möglichkeit gefunden, über mehrere Datenbankserver hinweg auf Datenbanken zuzugreifen. Im Netz kursieren einige Anleitungen, wie dies über Tricks möglich ist. Aber eine saubere Lösung sieht – leider – anders aus.

Zugriff auf MySQL-Server hinter einer Firewall

Heute müssen wir auf einen MySQL-Server zugreifen, der in einem lokalen Netz hinter einer Firewall steht. In diesem Fall ist es relativ einfach, denn das lokale Netz ist einfach ein privates Heimnetzwerk mit DSL-Anschluß. Hier ist zu beachten, dass durch den Internet-Provider jeden Tag eine neue IP-Adresse vergeben wird. Also müssen wir einen Dienst wie dyndns.org (LINK) nutzen, damit wir eine Verbindung zum Heimnetzwerk erhalten können, auch wenn wir die aktuelle IP-Adresse nicht kennen.

In unserem Beispiel hängt ein Netgear Router mit integrierter Firewall vor dem Heimnetzwerk. Damit wir von „außen“ auf den MySQL-Server kommen können, müssen wir die Firewall konfigurieren.

Port 3306 an der Firewall freigeben
Port 3306 an der Firewall freigebenWir wissen, dass der MySQL-Server auf den Port 3306 (Standard) lauscht. Man kann nächtelang mit Administratoren darüber diskutieren, ob es sinnvoll (und sicherer) ist, diesen Port freizugeben oder einen anderen Port für den Zugriff zu nutzen. Ich persönlich bin der Meinung, dass egal welcher Port offen ist, dieser natürlich eine Gefahr darstellt. Ob wir den Port 3306 oder 1234 nutzen: Ein Portsniffer wird auch diesen Port finden.
Ich lege dies in die persönliche Entscheidung, welchen Port man freigibt. In dieser Doku beschränke ich mich auf den Standard-Port von MySQL.

Bei den Netgear-Routern muss der Port über „Dienste“ zuerst angelegt werden. Wir tragen dort den Port „3306“ ein und geben ihm eine sinnvolle Bezeichnung wie beispielsweise „MySQL“.

Firewall: Port-Forwarding
MySQL Port ForwardingNun müssen wir der Firewall mitteilen, was mit Traffic am Port 3306 geschehen soll. Wir möchten, dass diese Kommunikation zum MySQL-Server geleitet wird. Hier ist wichtig, dass der MySQL-Server eine feste IP-Adresse im Netzwerk hat. Je nach Router kann hier auch eine dynamische Adresse eingegeben werden. Prinzipiell empfehle ich aber, einem Server immer eine statische Adresse zu geben.
Bei diesem Netgear-Router kann das Forwarding über „Regeln für die Firewall“ definiert werden. Mittels des Button „Hinzufügen“ fügen wir die neue Regel hinzu. Wir verknüpfen den zuvor angelegten „Dienst“ (Port 3306) mit der IP-Adresse des MySQL-Servers. Wie wir in der Hardcopy erkennen können, werden hier nur eingehende Dienste definiert. Ausgehende Dienste sind alle freigegeben. Auch hier kann man lange diskutieren, wie sinnvoll dies ist.
Es muss sichergestellt sein, dass der Port 3306 für eingehende wie ausgehende Dienste freigegeben wird.

Erster Test per Telnet
Nun könnte die Verbindung zum MySQL Server bereits funktionieren. Wir testen dies über Telnet. Bitte beachten, dass dieser Test nicht im lokalen Netzwerk, sondern von einem anderen Netzwerk aus gestartet werden muss (beispielsweise eine UMTS-Verbindung). Unter Windows startet man einem Command-Box über START/Ausführen/cmd.exe (oder Windows-R) und gibt dort „telnet“ ein. Dann „open meinhost 3306“, wobei „meinhost“ die DynDNS-Adresse ist.
Nach Bestätigen des Befehls startet der Verbindungsaufbau. Wenn eine Fehlermeldung wie „Es konnte keine Verbindung mit ….“ erscheint,  hat die Verbindung offensichtlich nicht funktioniert. Wir stattdessen ein wilder „Datenmüll“ (meist ist der Servername ersichtlich) angezeigt, hat die Verbindung funktioniert.

Freigabe („nach außen“) des MySQL-Server
Wenn die Verbindung nicht funktioniert hat, kann es daran liegen, dass der MySQL-Server noch keine Verbindungen außerhalb „localhost“ akzeptiert. Diese Einstellung macht aus Sicht der Sicherheit Sinn, muss jedoch hier geändert werden.