Memory bei Java-Zeichenketten

Es hat von Java 1.6 nach Java 1.7 eine für den Memoryverbrauch relevante Änderung gegeben.
Bis Java 1.6 wurde bei substring eine Zeichenkette konstruiert, die das selbe interne Array von Zeichen (char) referenziert und nur andere Werte für Anfang und Länge enthält. Das hatte den Vorteil, dass in vielen Fällen Memory gespart wurde, weil man die Teilzeichenkette nicht nochmal speichern musste. Auch das Umkopieren wurde gespart. Nun war es aber wichtig, in manchen Fällen so etwas wie
String mySubString = new String(origString.substring(3, 5));
statt
String mySubString = origString.substring(3, 5);
zu verwenden, wenn nämlich origString sehr viel länger als mySubString war und sehr viel früher obsolet wurde als mySubString. Wer das vergessen hat, hat so ein Memoryleak gebaut.
Das wurde in Java 1.7 „gefixt“, nun wird das Array mit den Zeichen intern immer kopiert, weil die Entwickler nicht verstanden haben, mit diesen beiden Möglichkeiten richtig umzugehen. Leider hat man nun aber die andere Möglichkeit nicht mehr zur Verfügung.
Was kann man tun, wenn man eine Memory-intensive Applikation schreiben will, in der viele langlebige Zeichenketten vorkommen, die Teil einer längeren, ebenfalls langlebigen Zeichenkette sind?
Die erste Frage ist immer, ob man mit der neue Implementierung nicht doch leben kann.
Da die Zeichenkette sehr tief in Java verankert ist, kann man sie nicht so leicht ersetzen bzw. muss etwas hässlicheren Code schreiben, um explizit die eigene Zeichenkette zu verwenden. Wenn nötig ist das aber machbar: Man nimmt von gnuClasspath java.lang.String und refactored das zu einem anderen Package- und Klassennamen. Daraus lässt sich dann leicht das gewünschte bauen. Wichtig sind Konversionsroutinen nach java.lang.String in beide Richtungen.

Links:

Share Button

Chemnitzer Linuxtage

Es gibt diese Veranstaltung nun schon seit 16 Jahren und ich war noch nie da…
Aber es muss gut sein:

Chemnitzer Linuxtage 2015

Share Button

Transaktionsisolation

English

Dieser Artikel basiert auf einem Vortrag, den ich bei der Ruby on Rails User Group Schweiz in Zürich gehalten habe.

Naiver Zugang zu Transaktionen

Alle reden von Transaktionen, aber wer kennt sich wirklich gut damit aus?
Ist es vielleicht wie beim Multithreading, wo man sehr viele Leute findet, die behaupten, sich gut auszukennen, aber sehr wenige Multithreading-Programme, die wirklich unter Last auch korrekt laufen?

Immerhin haben wir so eine ganz brauchbare Vorstellung:

  • Wir beginnen implizit oder explizit eine Transaktion
  • Wir führen eine oder mehrere Operationen durch (z.B. SQL-Befehle)
  • Wir beschließen am Ende entweder ein „roll-back“ oder „commit“
  • Roll-back stellt den Zustand von vor der Transaktion wieder her
  • Commit akzeptiert die Änderungen definitiv

Das ist doch überschaubar. Warum brauchen wir da noch komplizierten Kram?

Wenn wir uns in einer einfachen Welt bewegen, wo nur ein DB-User mit einer Session auf der Datenbank ist.

Die komplizierte Welt

In einer komplizierten Welt finden leider parallele Aktivitäten statt.
Mehrere Threads, Prozesse oder auch externe Datenbank-Clients greifen auf die Datenbank zu.

Was machst das so kompliziert?
Wenn Dinge parallel werden, dann ist jeder Entwickler gut darin, das im Griff zu haben, aber komischerweise funktioniert es dann meistens gar nicht oder zumindest nur auf dem Laptop des Entwicklers, nicht aber langfristig auf dem Server unter Last.
Aber die Leute, die Datenbankprodukte entwickeln, sind wirklich gut. Datenbanken laufen mit vielen parallelen Zugriffen unter Last auf Millionen von Rechner und funktionieren dabei noch recht zuverlässig.
Nun haben wir aber ein paar zusätzliche Fragen:

  • Was passiert mit unvollständigen Transaktionen vor dem Commit?
  • Was passiert mit erfolgreich abgeschlossenen Transaktionen während eines langen SELECTs oder einer langen lesenden Transaktion?
  • Dasselbe betrifft auch lange Transaktionen, die Daten verändern, aber dabei auch lesend zugreifen.

Eine Naive Anforderung wäre, dass andere Clients die Transaktion entweder ganz oder gar nicht sehen. Reciht das?
Man stelle sich ein Problem vor, das in einer langweiligen Informatik I-Vorlesung in meinem Studium real war:

  • Die Vorlesung geht 90 min ohne Pause.
  • Der „Hörsal“ ist eine Maschinenhalle, die man provisorisch mit Stühlen ausgestattet hat.
  • Etwa 600 Leute sind anwesend.
  • Es gehen dauernd Leute raus.
  • Am Schluss der Vorlesung ist der Hörsal immer noch ziemlich voll.
  • Wie viele Leute sind zu einer bestimmten Zeit in dem Hörsal?
  • Eine präzise und korrekte Zahl existiert immer, denn so brutale Sachen wie halbe Studenten waren damals nicht üblich.
  • Aber das Zählen ist schwierig, weil während dem Zählen dauernd Leute den Raum verlassen.
  • Merke: Das Verlassen des Hörsals ist eine Transaktion. Nach dem Commit ist die betreffende Person draußen, vorher drin.

Da kommt nun das berühmte Snapshot too old ins Spiel, das einige von uns, die noch mit Oracle arbeiten, recht gut kennen. Oracle hat das Problem, die Hörer im Hörsaal zu zählen, nämlich gelöst. Und ähnliche Probleme auch. Das SELECT bei Oracle bekommt für seine Verarbeitungszeit einen konsistenten Datenstand, der auf dem Zustand beim Start der Abfrage basiert.
Transaktionen, die währenddessen erfolgreich mit einem Commit abgeschlossen werden, werden nicht berücksichtigt. Das wird „Snapshot“ genannt. Sehr lange laufende SELECTs, die auf Daten zugreifen, die sich häufig ändern, scheitern manchmal mit „Snapshot too old“.

Präzisierung

Diese Konzepte werden jetzt etwas präzisiert, aber die Details nachzulesen sprengt sicher den Rahmen eines Blog-Artikels.
Es geht um:

  • Anomalien (Read phenomena)
  • Transaction-Isolation-Levels
  • ACID

Anomalien bezeichnen ein unerwartetes oder unwillkommenes Verhalten beim Lesen.

„Dirty Read“ bedeutet, dass auf Daten zugegriffen werden kann, die in einer anderen Transaktion geändert werden, bevor sie committed worden sind. Das widerspricht jeder intuitiven Vorstellung von Transaktionen. Eine interessante Frage ist aber, ob die Transaktion, die die Daten verändert, bei ihren Zugriffen jeweils den Stand nach den eigenen Veränderungen sehen kann oder ob dies erst nach dem eigenen Commit möglich ist. Bei Oracle galt immer der erste Fall, außer es hat sich kürzlich geändert, was ich aber stark bezweifle.

„Non-repeatable read“ steht für Leseoperationen auf denselben Daten während einer Transaktion, die aufgrund in anderen Transaktionen vorgenommer Änderungen verschiedene Ergebnisse liefern. Das ist genau das Problem mit dem großen Hörsal. Oracle vermeidet das zum Beispiel standardmäßig.

„Phantom reads“ stehen für Leseoperationen, bei denen zwar Daten sich selbst nicht ändern können, aber durch DELETE und INSERT Datensätze wegfallen oder hinzukommen können.

Isolationslevel bezeichen eine Einstellung einer Datenbank und die Anforderungen dazu.

„Read Uncommited“ erlaubt „dirty read“. Mir ist kein Fall bekannt, in dem das tatsächlich so praktiziert wird. Diese Einstellung ist für parallel mehrfach verwendete Datenbanken kaum brauchbar. Man kann das konfigurieren und der Standard erlaubt Datenbanken „besser“ als das zu sein.

„Read Committed“ steht dafür, dass man nur Daten von vollständig abgeschlossenen Transaktionen sehen kann. Das bedeutet also, das „dirty read“ ausgeschlossen ist, aber „non-repeatable read“ möglich ist. Das ist die Grundeinstellung für viele Datenbanken, z.B. für PostgresSQL.

„Repeatable Reads“ steht für eine Einstellung, die die Daten gegenüber UPDATEs scheinbar konstant hält. Das unterbindet „non-repeatable-read“, aber nicht „phantom-read“.

Bei der Einstellung „Serializable“ werden alle Transaktionen komplett entflochten. Man bekommt das Verhalten als wären alle Transaktionen in einer Warteschlange (Queue) und es würde immer nur jeweils eine nach der anderen abgearbeitet, womit wir wieder am Anfang wären, aber das stimmt nicht ganz. Zunächst mal werden sogar phantom-reads unterbunden.

Reale Datenbanken

Reale-Datenbanken haben eine Standardeinstellung („default“) für die Transaktionsisolation, aber sie dürfen immer besser als der eingestellte Wert sein, ohne den Standard zu verletzen. Das gibt also die Möglichkeit, Mischformen zu implementieren. Oracle hat mit „read-only“ sogar einen zusätzlichen Wert.

Nun gibt es verschiedene Wege, so etwas zu implementieren, grob gesagt sind es Locking oder Multiversion. Beim Locking werden Datensätze, Bereich, Spalten, Tabellen oder ganze Schemata gelockt, also exklusiv von einer Transaktion in Anspruch genommen. Bei Multiversion werden verschiedene Kopien der Daten erstellt und jeweils mit diesen gearbeitet. Es bleibt eine Herausforderung, die Daten am Schluss zusammenzufügen.

Verschiedene Datenbankprodukte nutzen den Interpretationsspielraum des Standards recht gut aus. Z.B. ist Oracle natürlich der Standard und man muss nur so tun, als halte man sich an den SQL-Standard.

Ich empfehle am Anfang eines Projekts eine weise Entscheidung über die Wahl der Datenbank zu treffen. Gerade hier bei den Transaktions-Isolations-Levels zeigt sich einmal mehr, dass die verschiedenen Datenbanken nicht so gleich sind und deshalb der Astausch gegen ein anderes Produkt problematisch und teuer sein kann.

ACID

ACID ist das englische Wort für Säure. In der Chemie nennt man Substanzen mit einen pH-Wert < 7 Säuren.
Hier ist aber „ACID“ ein Acronym:

  • Atomicity
  • Consistency
  • Isolation
  • Durability

Ein paar Links

Share Button

Transaction Isolation Levels

Deutsch

This blog post is based on a talk that I have given in the Ruby on Rails User Group.

Who knows Transactions?

Who knows really well what transactions are?

What is a Transaction?

Just common sense what transactions mean:

  • We open an transaction (implicitely or explicitely)
  • We perform a certain number of operations (for example SQL-statements)
  • We decide in the end to either „roll-back“ or „commit“
  • Roll-back restores the state before the transaction
  • Commit accepts the changes for good

That’s it?

  • So, that’s it, we are done…
  • Just scrap these stupid confusing advanced stuff, do it simple…

Simple world

  • In the simple world, only one DB-user at a time exists
  • Then we are really almost done..

Complicated World

  • In a complicated world we have parallel activities taking place:
  • Multiple threads, processes or external database clients simultanously using the database

Complications of Complication

  • What happens now?
  • When things get parallel, everybody is good in doing that, but it usually does not work.
  • So DB-developers are really good, because databases do actually sometimes work quite well, even under load…
  • What happens with incomplete transactions?
  • What happens with complete transactions during one complicated read query?

Naïve Requirement

  • Other clients should see an transaction either completely or not at all.
  • Is that enough?
  • Think of an auditorium for a boring lecture: 90 min, people continuously leaving the room (so boring), but at the end it is still quite full.
  • How many people are in the room?
  • Counting is hard, because more people leave during the count.

Snapshot too old

  • Oracle has solved that problem of counting the students and similar ones…
  • The SELECT-statement gets for its duration a consistent data content, based on what was in the DB when the statement started.
  • Transactions that have been committed successfully after starting the SELECT are not taken into account
  • This is called snapshot. Long SELECTs that work on data that is actively changed sometimes fail with „Snapshot too old“

Now the hardcore stuff

  • Read phenomena
  • Transaction Isolation Levels
  • ACID

Read Phenomena: Dirty Read

  • „Dirty read“ refers to uncommitted data being available for reading by other transactions. Sounds cool, right? Intuitive?
  • Interesting question: can you read your own uncommitted data within the running transaction?

Read Phenomena: Non-repeatable Read

  • „Non-repeatable read“ refers to a reading operations on the same piece of data that happen during the same transaction (or SELECT statement in the Oracle world) and give different results.
  • With Oracle this should not happen, we get snapshot too old instead.

Read Phenomena: Phantom reads

  • Data records are the same
  • But the set of records may change

Isolation Level: Read Uncommited

  • Allows „dirty Read“
  • I do not know of any case where this is done
  • You can configure it and may get „better“ than that

Isolation Level: Read Committed

  • Read data only from completed transaction
  • Non-repeatable read is possible
  • Default for many databases, like PostgreSQL

Isolation Level: Repeatable Reads

  • During one transaction data does not change
  • Phantom reads are still possible
  • Default for many databases, for example Oracle

Isolation Level: Serializable

  • Pretend all transactions are queued and only one at a time is happening
  • No phantom reads possible

Real databases

  • They have some default
  • But may always be „better“ than that
  • So mixtures are possible
  • For example Oracle has „read-only“ as isolation level…

Locking vs. Multiversion

  • Two mechanisms:
  • Locking of parts of the data
  • Multiversion: multiple copies (maybe copy on write?) around, for each transaction… Have fun when merging. 😉

Different DB products

  • Different DB products use the freedom to interpret the standard quite liberally
  • Oracle is the standard, they only pretend to care about SQL-standard…
  • Make a wise decision which DB to support with your software in the beginning
  • Remember: some commercial DB vendors might not have any customers, they just have hostages

ACID

ACIDs in chemistry are substances with a pH-value < 7.
Here acronym for

  • Atomicity
  • Consistency
  • Isolation
  • Durability

Some Links

Share Button

Früher ging alles schneller

Früher ging alles schneller, aber dann kamen die Computer und haben alles kompliziert gemacht.
Oder es wurden einfache Computer durch komplizierte ersetzt.

Nur zwei Beispiele:
Früher hatten die Leute beim Fahrkartenschalter der Bahn ein mechanisches Gerät, mit dem sie blitzschnell die gewünschte Fahrkarte generieren konnten. Die Fahrkarte war nur so groß wie zwei mittelgroße Münzen nebeneinander, passte also noch in das Gepäck. Und die Schlangen im Bahnhof waren noch kurz, weil alles blitzschnell ging.
Dann kamen Computer und die richtigen Schlangen. Die Computer hatten solche grünen Terminals und Masken, wo man mit ein paar Tasten die Fahrkarte bekam. Für den geübten Eisenbahner immer noch schnell. In der Bank hatten sie auch solche Terminals und man war auch dort schnell.
Dann kam aber Coolness ins Spiel. Die Arbeitsplätze bei Banken und Bahnschaltern bekamen grafische Oberflächen. Plötzlich waren die Schlangen lang und man brauchte viel Zeit.

Was sollte der Quatsch? Es ist wie mit Malerei und Fotografie. Der Maler, der heute fotografisch perfekt malen kann, ist nicht mehr so gefragt, das kann man mit digitalen oder analogen Kameras auch als Laie schaffen. Aber der Maler, der irgendwas Schönes malt, was man nicht so leicht fotografieren kann, ist noch interessant.
Fahrkarten und Geldabhebungen und Überweisungen und Überprüfung des Kontostands kann man heute an Automaten oder im Internet erledigen. Oder mit dem Mobiltelefon.
Der Schalter bekommt eine andere Funktion, denn dort kann man die Spezialitäten bekommen, die weder die grün-schwarze Terminal-Maske noch die coolste Mobile-App bieten können. Auslandsfahrkarten waren noch in den 80er-Jahren sehr schwierig zu kriegen. Man musste zum Sonderschalter, das Geld vorauszahlen und dann wurde die Reservierung per Telex oder Fax an die Partnerbahn geschickt und wenn man Pech hatte, klappte es dann doch nicht. Heute kauft man die einfach.

Share Button