Unglaublich, aber heute hat mich Java zum ersten Mal in meinem Leben enttäuscht:-(
Wie die Überschrift des Blogeintrags verrät, habe ich Ungenauigkeiten in der Zeitmessung von Java festgestellt. Normalerweise verwende ich Timestamps selten als Primery-Key in der Datenbank, aber wenn die eigentliche Verarbeitung unter 10 Millisekunden dauert, kann das zu Problemen unter Windows XP führen! Laut API bestimmt das darunter liegende Betriebssystem wie oft der Zeitwert aktualisiert wird. Mit anderen Worten: Windows XP (32 Bit) und Java Version: JDK 1.6.0_16 aktualisieren im Durchschnitt aller 15 ms die Systemzeit. Somit erhält man in meinem Beispiel(Datenbank-Primery-Key) zweimal den gleichen Timestamp und provoziert damit einen SQL-Fehler. Dabei spielt es keine Rolle, ob man die Funktion System.currentTimeMillis(); oder das Objekt new Date(); verwendet.
package main; import java.text.SimpleDateFormat; import java.util.Date; import java.util.TimeZone; public class Main { /** * @param args */ public static void main(String[] args) { for (int i = 0; i < 25; i++) { Date myDate = new Date(); System.out.println(format(myDate)); System.out.println(System.currentTimeMillis()); System.out.println("+++"); } } public static String format(Date myDate){ SimpleDateFormat dateFormat = new SimpleDateFormat("ss-SSS"); dateFormat.setTimeZone(TimeZone.getTimeZone("GMT+1")); return dateFormat.format(myDate); } }
Der Test wurde mit derselben Hardware und der gleichen JDK Version (1.6.0_16) durchgeführt. Das Test-Programm zeigt einen deutlichen Unterschied zwischen Windows XP und Windows 7. Unter Windows XP wird in der Regel aller 15 ms die Systemzeit aktualisiert. Im Gegensatz dazu scheint Windows 7 wirklich jede Millisekunde die Systemzeit zu aktualisieren. Weitere Tests sind für Open Suse und Red Hat geplant.
Es gibt verschiedene Möglichkeiten die Zeit in Java zu messen. Zwei davon stelle ich hier vor:
Die Funktion System.currentTimeMillis() gibt die aktuelle Systemzeit in Millisekundenbereich zurück, jedoch hängt der tatsächliche Aktualisierungsrhythmus von dem eingesetzten Betriebssystem und dessen Konfiguration ab. Der zurückgegebene Wert bezieht sich auf die seit dem 01.01.1970 00:00 (UTC) vergangenen Millisekunden. Die genaue Definition der Funktion finden Sie in der Java API (http://docs.oracle.com/javase/7/docs/api/java/lang/System.html#currentTimeMillis%28%29). Somit eignet sich diese Funktion aufgrund ihrer möglichen Ungenauigkeiten nur bedingt für eine Performance-Messungen, jedoch kann man weiterhin eindeutige Timestamps mit dieser Funktion erzeugen, wenn die Bearbeitungszeit über 20 ms liegt.
Mit der Java-Version 1.5 wurde die Funktion System.nanoTime() eingeführt. Diese Funktion wählt die genaueste verfügbare Zeitquelle aus und gibt deren Wert in Nanosekunden zurück. Der Bezugszeitpunkt der Zeitangabe oder die tatsächliche Genauigkeit bleiben jedoch unbekannt. Aus diesen Gründen können die Ausgaben der Funktion nur zur Berechnung von bereits verstrichener Zeit benutzt werden. Weitere Informationen finden Sie in der Java API (http://docs.oracle.com/javase/7/docs/api/java/lang/System.html#nanoTime%28%29). Die Funktion System.nanoTime() bietet derzeit die beste Möglichkeit um eine Performance-Messungen durchzuführen.
Diese Tatsache ist dokumentiert und die Nutzung eines Timestamp als primary key halte ich für grob fahrlässig…
Als „grob fahrlässig“ würde ich das nicht einstufen, aber auf einen Timestamp als einzigen P-Key würde ich mich auch nicht verlassen.
Es kommt auf die Situation an.
Doofe Frage, aber: Warum einen Timestamp als PK nehmen?
Nicht fragen-.-“
Aber die Bundesanstalt für Finanzdienstleistungsaufsicht (BaFin) zum Beispiel schreibt explizit einen Timestamp als PK vor…