Nudge am 01.04.2009

MySQL Sequence oder integer-Timestamp

in MySQL | Tags: Datenbank, MSSQL, MySQL, Postgresql, Replikation, Routine, Sequenz, Synchronisation, Timestamp, Trigger, Update

Ich suche seit einiger Zeit nach einer bequemen Möglichkeit, einen MSSQL-ähnlichen Timestamp in MySQL zu integrieren.

Im Unterschied zu MySQL benutzt MSSQL nämlich nicht das aktuelle Datum, sondern einen (binär codierten) integer-Wert. Dieser wird kontinuierlich im Laufe des MSSQL-Lebenszyklus hochgezählt. Das sieht erst einmal nicht so schön aus – man weiß also nicht, wann der Datensatz zuletzt angefasst wurde. Dennoch bringt er einen kleinen Vorteil mit: Für einfache Synchronisationsaufgaben braucht man sich nur den letzten abgerufenen Wert zu merken. Erst alle höheren Timestamps müssen neu übertragen werden. Bei MySQL muss man alle Datensätze der letzten Sekunde wieder übertragen, denn eine Sekunde ist im Leben einer Datenbank eine Ewigkeit, da kann viel passieren.

Warum sollte man denn überhaupt über Zeitstempel synchronisieren?

Nun, das Leben bietet mehr als eine Datenbank, und ab und zu muss man einen Datensatz über mehrere Datenbanken synchron halten. Da die verfügbaren Mechanismen der Replikation sich allesamt auf das jeweilige Datenbank-Produkt beziehen, hat man keine andere Chance. Man könnte natürlich auch Spalten für Geändert/Gelöscht in den Tabellen pflegen. Doch man benötigte dazu Rechte, das Tabellenschema zu ändern und die Applikation zu modifizieren. Und es müsste ausnahmslos und sauber programmiert sein. Ein Datenbank-Zeitstempel dagegen befreit von diesen Zwängen, arbeitet vollkommen automatisch und transparent für den Benutzer. Und mit einem Zeitstempel à la Timestamp hat man schon fast alles, was man zur Synchronisation braucht.

Alternativen zum Timestamp mit gleichem Mechanismus

Die erste offensichtliche Möglichkeit habe ich in einer Sequenz gesehen. Diese ist wiederum nicht ganz so einfach abzubilden wie in Postgres, was ja bekannt dafür ist. Sondern man programmiert sich eine per CREATE FUNCTION eine Routine, die wiederum in einer eigenen Tabelle einen Wert inkrementiert, optimalerweise gleich einen BIGINT, denn es soll ja mehr als 3 Tage funktionieren.

Für jede Tabelle, die man mit der Sequence ausstatten möchte, muss man zwei Trigger anlegen, einmal BEFORE INSERT und einen BEFORE UPDATE. Sie setzen dann jeweils per NEW.Timestamp = f_getSeq() einen neuen Sequence-Wert ein. Es kann dabei durchaus passieren, dass zwei Datensätze denselben Sequence-Wert bekommen, da die Routine nicht-atomar ist. Da aber MyISAM Locking nur auf Tabellen-Ebene betreibt, können diese nur auf zwei unterschiedlichen Tabellen landen – folglich ist diese Einschränkung für die Synchronisationsaufgabe nicht entscheidend. Doch spätestens, wenn man 200 oder mehr Tabellen im Einsatz hat, wird die Sache mit den Triggern extrem unübersichtlich.

Ausweg zwei: Genauere Zeitstempel mit Microsekunden

Angeblich kann MySQL ja Timestamps mit Microseconds ausgeben. Bei mir kommt dort immer .000000 raus. Kann natürlich auch an einer zu schwachen CPU oder an den zu langen Prozess-Zyklen des Kernel liegen. Wenn man davon ausgehen kann, dass dies funktioniert, wäre es an sich eine gute Sache: Lesbar und totzdem extrem genau. Es muss dann auf jeden Fall sichergestellt sein, dass pro Satz in einem Update ein neuer Wert erzeugt wird, und man sollte davon ausgehen, dass innerhalb einer Microsekunde nicht zu viel passiert. Für Hochlastserver also nix.

Postgres hat meines Wissens die Microsekunden standardmäßig im Zeitstempel drin. Auf den Chemnitzer Linux-Tagen habe ich mal mit einem der Postgres-Leute über diese Variante von Synchronisation gesprochen. Er meinte aber, dass dies nicht Locking-safe sei. Es könnte also passieren, dass ein Update erst später geschrieben als sein Zeitstempel erzeugt wird, denn die Zieltabelle könnte in diesem Moment gesperrt sein. So könnten sich zwei Updates überlagern und der letzte trotzdem einen früheren Zeitstempel erhalten. Synchronisiert man aber nicht zu oft, sollte das kein Problem darstellen.

Naja, vielleicht sollte man mal einen Feature Request für MySQL 6.x stellen? Aber bevor das in den Server-Distributionen dann drin wäre, bin ich Rentner. Bis dahin müssen halt noch ein paar Datensätze doppelt übertragen werden.


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

Leave a Reply

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