Das diesem Dokument zugrundeliegende Vorhaben wurde mit Mitteln des Bundesministeriums für Bildung und Forschung unter dem Förderkennzeichen 16OH21005 gefördert. Die Verantwortung für den Inhalt dieser Veröffentlichung liegt beim Autor/bei der Autorin. Nachdem wir schon Transaktionen kennengelernt haben, geht es hier um den sogenannten Isolation Level. Hier werden wir das Thema Transaktionen und das isolierte Ausführen von Anwendungen noch weiter im Detail untersuchen. 1 Zunächst wollen wir klären, was Isolation-Level bedeutet und welche Probleme zu lösen sind. Da ja das parallele Aufführen von Anwendungen im Mittelpunkt steht, müssen wir uns mit dem Thema Nebenläufigkeiten beschäftigen. Nebenläufigkeiten bedeutet, welche Situationen auftreten können, während der gleichzeitigen /parallelen Ausführung von Anwendungen, die auf den gleichen Daten arbeiten. Hierbei spielen Datenbanksperren eine wichtige Rolle. Nachdem dann die theoretischen Grundlagen gelegt sind, wenden wir uns wieder der Programmiersprache SQL zu. 2 Im letzten Teil geht es wieder etwas praktischer zu, und wir beschäftigen uns mit den SQL-Anweisungen, die uns helfen sollen mit Datenbanksperren umzugehen bzw. sie beeinflussen zu können. Hinweis: Der Umgang mit Datenbanksperren führt in der Praxis immer wieder zu Problemen, da diese einen enormen Einfluss auf die Performance einer Anwendung haben können. Daher ist es unbedingt notwendig sowohl für Datenbank –Administratoren, als auch für Datenbank-Anwendungsentwickler, sich mit den verschiedenen Isolation-Level und den damit verbundenen Sperr-Konzepten (DB-Locks) auseinanderzusetzen. 3 Nun endlich zur ersten Frage: Was ist ISOLATION-LEVEL eigentlich? Wie schon in dem Abschnitt Transaktion beschrieben, geht es hier wiederum um Mehrbenutzerfähigkeit. Wir betrachten auch hier wieder, was passiert, wenn mehrere Benutzer gleichzeitig auf Daten zugreifen. Bei den bisherigen Übungen und Überlegungen im Abschnitt haben wir uns keine Gedanken gemacht über eventuelle Datenbanksperren und warum ein Benutzer Änderungen eines anderen Benutzer nicht „sehen“ bzw. mitgeteilt bekommt. Genau dies wollen wir in diesem Abschnitt genauer untersuchen. 4 Schauen wir uns mal folgendes Beispiel an. Zwei Personen führen SQL Operationen auf der gleichen Datenbank aus. Die Person auf der linken Seite startet eine Update Operation auf eine Zeile in der Tabelle KONTO, um den Kontostand zu ändern. Eine weitere Person auf der rechten Seite will den Kontostand ermitteln und führt daher eine Select Operation aus. Nun stellt sich die Frage: Welcher Kontostand wird der zweiten Person angezeigt? Um diese Frage zu beantworten, müssen wird uns näher beschäftigen, welche Mittel es gibt, um mit diesen sogenannten Nebenläufigkeiten umzugehen. 5 In der Abbildung sehen wir ein Szenario, welche mit Titel „UPDAET LOST“ beschrieben bzw. zusammengefasst werden kann. Sie sehen ein kleines UML Sequenz-Diagramm. Hierbei kennzeichnen die senkrechten Striche die Zeitachse. Die Aktionen erfolgen dabei von oben nach untern ( Zeitpunkt t1 ist vor Zeitpunkt t3). Bei der Kommunikation sind 3 Parteien beteiligt. Links sehen Sie ALICE. Sie steht repräsentativ für eine Anwendung, welche eine SELECT Anweisung auf eine ganz bestimmte Tabelle ausführt. Auf der rechten Seite sehen Sie BOB. Er steht repräsentativ für eine weitere Anwendung, welche ebenfalls eine SELECT Anweisung auf die gleiche Tabelle ausführt. In Verbindung mit der Zeitachse sehen wir folgende Sequenz / Abfolge: Die Sequenz: 1. Zunächst führt ALICE einen SELECT ausführt, um die Tupel-Menge P zu lesen 6 2. Danach führt BOB einen SELECT aus, um die Tupel-Menge P zu lesen 3. Danach führt ALICE einen UPDATE auf die Tupel-Menge P durch. 4. Anschließend führt BOB ebenfalls einen Update auf die Tupel-Menge P durch. Aus diesem Szenario geht hervor, dass die Änderungen von ALICE verloren gehen!!!! Der Grund liegt darin, das BOB nicht informiert war, dass ALICE die gleichen DATEN kurz vorher geändert hatte. Dies ist aber leider nicht das einzige Problem. Schauen wir uns ein weiteres Szenario an. ( Wir merken uns das UPDATE LOST Szenario für später). Hinweis: • Wenn Sie mit UML Sequenzdiagrammen nicht vertraut sind, finden Sie eine gute Beschreibungen unter http://www.ibm.com/developerworks/rational/library/3101.html 6 In der Abbildung sehen wir ein Szenario, welche mit Titel „DIRTY READ “ beschrieben bzw. zusammengefasst werden kann. Hier sehen wir wieder ein kleines UML Sequenz-Diagramm. Beteiligt sind wieder ALICE und BOB In Verbindung mit der Zeitachse sehen wir folgende Sequenz / Abfolge: Die Sequenz 1. Zunächst führt ALICE einen UPDATE auf die Tupel-Menge P aus 2. Danach führt BOB einen SELECT aus, um die Tupel-Menge P zu lesen. Hier sind die Änderungen von ALICE enthalten. Er sieht also die Änderungen von ALICE. 3. Danach führt ALICE einen ROLLBACK aus. Dadurch werden ihre vorherigen Änderungen wieder rückgängig gemacht Aus diesem Szenario geht hervor, dass BOB nun mit falschen Daten weiterarbeitet. 7 Es gibt noch leider noch weitere Szenarien. Schauen wir uns ein weiteres Szenario an. ( Wir merken uns das DIRTY READ Szenario für später). 7 In der Abbildung sehen wir ein Szenario, welche mit Titel „NON-REPEATABLE READ “ beschrieben bzw. zusammengefasst werden kann. Hier sehen wir wieder ein kleines UML Sequenz-Diagramm. Beteiligt sind wieder ALICE und BOB In Verbindung mit der Zeitachse sehen wir folgende Sequenz / Abfolge: Die Sequenz 1. Zunächst führt ALICE einen SELECT durch, um die Tupel-Menge P zu lesen 2. Danach führt BOB ebenfalls einen SELECT aus, um die Tupel-Menge P zu lesen. 3. Danach führt ALICE einen UPDATE durch, um die Tupel-Menge P zu ändern. 4. Jetzt führt BOB nochmals den gleichen SELECT wie vorher. 5. Nun führt ALICE einen COMMIT aus, um die Daten persistent zu machen . Die Transaktion wird beendet. Aus diesem Szenario geht hervor, dass bei dem zweiten Lesen ganz andere Werte erhält als vorher. 8 Und es gibt noch ein weiteres Szenario. Schauen wir uns ein diese Szenario auch noch an. ( Wir merken uns das NON-REPEATABLE-READ Szenario für später). 8 In der Abbildung sehen wir ein Szenario, welche mit Titel „NON-REPEATABLE READ “ beschrieben bzw. zusammengefasst werden kann. Hier sehen wir wieder ein kleines UML Sequenz-Diagramm. Beteiligt sind wieder ALICE und BOB In Verbindung mit der Zeitachse sehen wir folgende Sequenz / Abfolge: Die Sequenz 1. Zunächst führt ALICE einen SELECT aus und erhält zum Beispiel 5 Zeilen (Tupel-Menge X) 2. Danach führt BOB ebenfalls einen UPDATE aus, bei dem Attributwerte geändert werden, die bei dem SELECT von ALICE in der WHERE Bedingung genannt wurden. 3. Danach führt ALICE erneut den einen SELECT durch und erhält nun eine ganz andere Ergebnismenge. Anstatt wie vorher 5 erhält Sie nun 8 Zeilen Aus diesem Szenario geht hervor, dass bei dem zweiten Lesen ganz andere Ergebnis-Menge gelesen wird. ALICE erhält Daten, die vorher scheinbar gar nicht vorhanden waren. 9 Nachdem wir nun vier Szenarien betrachtet haben, stellt sich die Frage: Wie kann ich die Problem aus den Szenarien lösen? Genau dieser Frage wollen wir als nächstes nachgehen. 9 Um die Probleme aus den genannten Szenarien lösen zu können, wurde das Konzept der Datenbanksperren eingeführt. Wie wir bereits wissen, verändern folgende die SQL Anweisungen Daten einer Tabelle: • INSERT • UPDATE • DELETE Durch eine sogenannte Datenbanksperre ( Lock) wird nun angezeigt, ob ein Datensatz (Zeile) geändert wurde oder nicht. Dadurch wird allen Anwendungen die Möglichkeit gegeben, festzustellen, ob Daten verändert wurden oder nicht. Die Abbilddung zeigt ein Beispiel für die einfachste Art von Sperren. Nämlich, dass es für jede Zeile in einer Tabelle möglich ist eine Sperre (LOCK) zu setzen oder entfernen. HINWEIS: 10 Später werden wir noch sehen, dass es noch andere arten von Sperren gibt. Für das Verständnis von Sperren genügt es , erst mal so zu tun, als gäbe es nur Sperren für eine Zeile in einer Tabelle. 10 Schauen wir uns nun an, was mit Locks alles machen kann und wie die in der Praxis eingesetzt werden. Bisher haben wird uns um Locks nicht gekümmert. Wir haben bisher immer angenommen, dass unsere Änderungen sofort in der Datenbank vorgenommen werden. Dies ist immer dann der Fall, wenn wir in den sogenannten „AUTO COMMIT“ Mode arbeiten. In den meisten DBMS ist dies der Default-Fall, sobald man eine Verbindung (Datenbank-Sitzung, engl. Database session) aufgebaut hat. Will man nun erreichen, dass bei einem Update ( vgl. Abbildung) ein anderer Benutzer die Änderung angezeigt bekommt, dann muss eine entsprechende Sperre gesetzt werden. Dies erreicht man dadurch, dass man den AUTO-COMMIT Mode ausschaltet. Set autocommit off; Das kennen wir schon aus dem Abschnitt Transaktionen. 11 Nun stellen sich drei Fragen: 1. Wie kann ich beeinflussen, dass innerhalb einer Transaktion nun eine Sperre gesetzt werden soll oder nicht? Hier geht es um den Isolation Level 2. Wie werden die Sperren verwaltet? Für jedes Tupel einzeln oder geht es Tabellenweise? Es geht also um die Granularität 3. Gibt es unterschiedliche Arten von Locks? Kann man an einer Sperre ( Lock) erkennen, ob ein Satz gelesen, update oder gelöscht wurde? Es geht als um Lock Arten? Wenden wir uns zunächst erst einmal der ersten Frage nach dem Isolation Level zu. 11 Um festzulegen, welche Sperren gesetzt werden sollen, kann mittels eine SQL Anweisung den sogenannten Isolation Level setzen. Dies setzt man meist am Anfang einmal, nachdem man eine Verbindung zur Datenbank aufgebaut hat. In besonderen Fällen kann man auch Isolation Level individuell für eine Transaktion setzen. Aber zunächst wollen wir uns mit diesem Detail nicht aufhalten und erst einmal grundsätzlich verstehen: • Wie kann ich den ISOLATION Level setzen ? • Welche LEVEL gibt es überhaupt. In dieser Abbildung sehen wir die Syntax des SQL Befehls zum Setzen des Isolation Levels. Wie Sie sehen unterscheidet man vier verschiedene Level • Read uncommitted 12 • Read committed • Repeatable read • Serializable Welche Level man in der Praxis verwenden soll und warum sehen wir gleich in der nächsten Abbildung. Beachte: SQL Anweisung ist nicht bei jedem Hersteller gleich! Wie hier auf die Gemeinsamkeiten, die alle Datenbanken unterstützen. 12 Wie wir bereits wissen, dann mittels der SQL Anweisung SET ISOLAOTION LEVEL der Isolation Level gesetzt werden. In dieser Abbildung sehen Sie nun eine kleine Tabelle, aus welcher hervorgeht, welches der vorher genannten Probleme man ‚lösen möchte‘. Sie erinnern sich Die Szenarien waren ja: • Update lost • Dirty Read • Non-repeatable read • Phantom read In der Tabelle ist zu entnehmen, bei welchem Isolation Level , welches Problem gelöst werden kann. Nachdem wir nun einen Überblick über die Isolation Level verschafft haben, bleibt noch die Frage nach der Granularität der Sperren. Wie bereits schon angedeutet, kann man Sperren nicht nur auf Zeilen setzen, sondern auch auf andere Objekte. Dies wollen wir uns als nächstes ansehen. 13 Wichtig ist an dieser Stellen, dass wir uns merken, dass wir aus einer Anwendung heraus den Isolation Level setzen können und das in Abhängigkeit des Isolation Level Sperren gesetzt werden, um die beschriebenen Probleme zu lösen. Daher müssen wir uns als nächstes noch ansehen, auf welche Objekte nun Sperren gesetzt werden können, Setzt man den Isolation Level auf • Read uncommitted , so vermeidet man nur das Update Lost Problem • Read committed, vermeidet man „Update lost“ und „Dirty Read“ Probleme • Repeatable Read, vermeidet man, „Update Lost“, „Dirty Read“ und „Non Repeatable Read“ Probleme • Serializable, vermeidet alle Problem Wie man schon an dem Namen „Serializable“ erahnen kann, erzwingt man dadurch, dass alle SQL Anweisungen aller Anwendungen sequentielle ausgeführt werden. Dies behebt zwar alle Probleme die durch Nebenläufigkeiten entstehen können, dafür geht dies aber erheblich zu Lasten der Performance / Antwortzeiten. 13 Bisher haben wir immer gedacht , dass Sperren für eine Zeile einer Tabelle gesetzt oder gelöscht werden. Wir sind dabei davon ausgegangen, dass durch eine UPDATE, INSERT, DELETE Anweisung eine Sperre gesetzt wird. Das Rücksetzen der Sperren erfolgt, sobald eine Transaktion geschlossen wurde. Entweder durch Commit oder durch ROLLBACK. Um die Granularität zu verstehen, müssen wir uns etwas näher mit der Implementation von Datenbanken beschäftigen. Wie aus der Abbildung ersichtlich wird, bietet jedes Datenbanksystem min. drei verschiedene Ebenen der Granularität. • Die erste Ebene wird als ROW-LEVEL bezeichnet und gibt an, dass für jede Zeile in einer Tabelle eine Sperre gesetzt wird. • Aus Performancegründen werden Sätze aus einer Tabelle nicht zeilenweise gelesen. Stattdessen werden immer gleich mehrere Zeilen einer Tabelle in den Hauptspeicher gelesen unabhängig davon, ob die Anwendung nur eine Zeile oder mehrere Zeilen benötigt. Die Zeilen werden also in Blöcken gelesen und im Hauptspeicher in sogenannten Pages (Seiten) abgelegt. Sind nun für eine 14 Tabelle sehr viele Sperren zu verwalten, so verwendet man sogenannte Sperren auf Pages. Damit sinkt die Anzahl der zu verwaltenden Sperren. Dies bedeutet aber auch, dass ein Page.-Sperre (Page-Lock) ALLE Zeilen sperrt, die in einer Page abgelegt sind. • Da wird es in der Praxis häufig mit sehr großen Mengen an Daten zu tun haben, kann es vorkommen, dass es für eine Tabelle sehr viele Pages im Hauptspeicher gleichzeitig vorgehalten werden müssen. Somit gibt es unter Umständen sehr viel Page Locks. Um den Verwaltungsaufwand für die Page-Locks zu reduzieren, geht man dazu über ganze Tabellen zu sperren. Dies bedeutet, das bei einem Table-Lock ALL Zeilen einer Tabelle gesperrt sind. Hinweis: Im Abschnitt Systemarchitektur, werden wird auf das Thema Tabellen und Pages zurückkommen. 14 In dieser Abbildung sind nochmals die einzelnen Ebenen abgebildet. Die Besonderheit hier ist, dass die Ebene Datenbank hinzugekommen ist. Wenn es zu viele Sperren auf Tabellenebene gibt, gehen manche Systeme dazu über, eine Sperr für eine ganze Datenbank zu setzen. Kommen wir wieder auf das Wesentlich zurück und schauen uns in der nächsten Abbildung den Übergang der einzelnen Ebene nochmal im Überblick an, 15 Diese Abbildung gibt Ihnen nochmal eine graphischen Überblick über die einzelnen Sperr-Ebenen. Wie Sie sehen, bietet eine Sperre auf Datenbank-Ebene das geringste Maß und paralleler Ausführung, dafür aber den geringsten Verwaltungsaufwand für das Verwalten der Sperren. Am anderen Ende sehen Sie, dass Sperren auf Zeileneben das höchste Maß und paralleler Ausführung bietet, jedoch ist der Aufwand für das Verwalten der Sperren entsprechend hoch. 16 Die gute Nachricht ist, das wir uns bei der Entwicklung einer Anwendung darüber keine Gedanken machen müssen. Deshalb gibt es ja DatenbankManagement Systeme. In der Abbildung sehen Sie nochmals zusammengefasst die einzelnen Eskalationsstufen bei der Verwaltung der einzelnen Sperr-Ebenen. Wichtig an dieser Stelle ist nur, dass wir wissen, dass es diese Möglichkeiten gibt und dies natürlich einen Einfluss auf die Gesamt-Performance haben kann. Nachdem wir gelernt haben • Einen Isolation Level zu setzen um bestimmte Probleme zu beheben • Dass die Sperren unterschiedliche Granularität haben können und dies alleine von dem DBMS verwaltetet werden Wollen wir als nächstes der Frage nach gehen Was sind Sperren genau und gibt es unterschiedliche Arten von Sperren? 17 Was sind Sperren genau und gibt es unterschiedliche Arten von Sperren? Schauen wir uns als erstes an was Sperren sind? Aus Anwendungssicht hat eine Sperre einen Boolean Wert. Ist eine Sperre gesetzt, so hat sie den Wert TRUE und bedeutet, dass z.B. eine Zeile eine Tabelle für Anwendungen nicht zugreifbar sind. Aus der Sicht einer Datenbank stellt die Sperre eine Semaphore dar. Diese hat die gleiche Bedeutung, wie wir sie von Multitasking-Betriebssystem her kennen, um ein Objekt vor parallelen Zugriffen zu schützen. Stichwirt: „Mutual Exklusive“ kurz MUTEX genannt. Die Frage ist nun, welche Bedeutung hat eine Sperre, wenn diese gesetzt ist und was bedeutet dies für eine Anwendung? Hierzu schauen wir uns die unterschiedlichen Arten von Sperren an, 18 In diesem Kurs verwenden wir den Begriff „Lock Arten“. In der Literatur findet man aber auch andere Begriffe. In manchen Blogs, Büchern, Magazinen, usw. werden dann Begriffe wie • Lock Mode • Lock Type Verwendet. All diese Begriffe beschreiben aber den gleichen Sachverhalt. Je nachdem, um welche Art von Lock es sich handelt, unterscheidet man zwischen folgenden Locks: 1. Shared Lock 2. Update Lock 3. Exclusive Lock Was bedeuten nun die einzelnen Arten? Dieser Frage gehen wir als nächstes nach. 19 Was bedeuten nun die einzelnen Arten? Wie in der Abbildung dargestellt, unterscheiden sich die einzelnen Arten nur darin, aufgrund welchen Ereignisses die Sperre gesetzt wird. Shared Lock Ein Shared Lock wird zum Beispiel für eine Zeile immer dann gesetzt, wenn eine Anwendung eine Zeile gelesen hat. Also, durch SELECT Anweisungen Damit wird angezeigt, dass die Zeile gelesen wurde. Diese Zeilen können von anderen Transaktionen gelesen werden. Exclusive Lock Ein EXCLUSIVE Lock wird zum Beispiel für eine Zeile immer dann gesetzt, wenn eine Anwendung eine Zeile eingefügt, gelöscht oder geändert hat. Also, durch INSERT, DELETE , UPDATE Anweisungen. Bestand für diese Zeile bereits ein „Shared Lock“ , so wird der „Shared Lock“ zu einem „Exclusive Lock“ umgewandelt. 20 Die einzelnen Datenbank-Implementation unterscheiden noch weitere unterschiedliche Arten von Locks. Die Namen der Lock-Arten sind dementsprechend auch Hersteller spezifisch. Zum Nachlesen: • Oracle: https://docs.oracle.com/cd/E11882_01/server.112/e41084/ap_locks001.htm#SQLRF55 502 • SQL Server: https://technet.microsoft.com/de-de/library/ms175519(v=sql.105).aspx Hinweis: • Vgl. SQL Server https://technet.microsoft.com/dede/library/ms175519(v=sql.105).aspx • Vgl. ORACLE http://docs.oracle.com/cd/E11882_01/server.112/e41084/ap_locks001.htm#SQLRF555 02 • 20 Kenntnisse über die Lock-Arten und deren Verwendung sind für den Multi-UserBetrieb sehr wichtig, da diese einen wesentlichen Einfluss auf das Sperrverhalten und damit auf die Performance haben. Trifft eine Transaktion nämlich auf ein „Exclusive-Lock“, so muss die Transaktion warten, bis die Sperre gelöscht wird. Wie wir an der Abbildung erkennen können, kann dies unter Umständen zu sogenannten Deadlock führen. Schauen wir uns die Abbildung genauer an: 1. User ALICE führt einen Update auf einen Satz X in einer Tabelle A durch. Dies führt dazu, dass eine Sperre für den Satz X gesetzt wird. 2. Nun führt Bob einen Update auf einen SATZ Y in einer Tabelle B durch. Dies führt zu einer Sperre für den Satz Y in der Tabelle B. 3. Als nächstes möchte ALICE den SATZ Y in der Tabelle B auch ändern und trifft auf die Sperre von Bob. Dies bedeutet, dass die Transaktion „blocked“ ist. Dies bedeutet, dass die Transaktion warten muss bis die Sperre gelöscht wird. 4. Wenn nun BOB seinerseits versucht den SATZ X in der Tabelle A zu ändern, trifft dieser auf die Sperre von ALICE und muss nun ebenfalls warten. 21 5. Nun sind beide „blocked“ und warten gegenseitig aus sich. Die sogenannte DEADLOCK Situation Für diese Fälle haben die meisten Datenbankhersteller eine „Deadlock-Erkennung“ eingebaut. Wird ein Deadlock erkannt, so werden in allen beteiligten Transaktionen ( in unseren Fall ALICE und BOB) ein Fehlergemeldet mit dem Hinweis, dass ein Deadlock erkannt wurde. Somit sind die Applikation nicht mehr „blocked“ und können entsprechend reagieren. In der Praxis führen beide Applikationen eine ROLLBACK durch, damit alle Sperren gelöscht werden und können erneut die Transaktion von Anfang an wiederholen. 21 Um nun solche Arten von Deadlocks möglichst zu vermeiden, sollten die Zugriffe auf die Tabellen in den unterschiedlichen Transaktionen immer in der gleichen Reihenfolge erfolgen. 22 23 24