Transaktionsisolationsstufen für die Kleinsten



Heute möchte ich einen äußerst interessanten, aber oft mit Geheimnissen für gewöhnliche sterbliche Programmierer abgedeckten Abschnitt der Datenbank (DB) mitbringen - Transaktionsisolationsstufen. Wie die Praxis zeigt, haben viele mit der IT verbundene Personen, insbesondere mit der Arbeit mit Datenbanken, wenig Verständnis dafür, warum diese Ebenen benötigt werden und wie sie zu ihrem eigenen Vorteil verwendet werden können.

Ein bisschen Theorie


Transaktionen selbst erfordern keine besonderen Erläuterungen. Bei einer Transaktion handelt es sich um N (N≥1) Abfragen an die Datenbank, die alle zusammen oder überhaupt nicht erfolgreich ausgeführt werden. Die Transaktionsisolation zeigt, wie stark sich parallele Transaktionen gegenseitig beeinflussen.
Bei der Auswahl einer Transaktionsebene versuchen wir, einen Konsens bei der Wahl zwischen einer hohen Datenkonsistenz zwischen Transaktionen und der Geschwindigkeit dieser Transaktionen zu erzielen.
Es ist anzumerken, dass die höchste Ausführungsgeschwindigkeit und die niedrigste Konsistenz nicht festgeschrieben gelesen werden . Die niedrigste Ausführungsgeschwindigkeit und höchste Konsistenz ist serialisierbar .

Umweltvorbereitung


Zum Beispiel wurde das MySQL DBMS ausgewählt. PostgreSQL könnte ebenfalls verwendet werden, unterstützt jedoch nicht die nicht festgeschriebene Isolationsstufe zum Lesen und verwendet stattdessen die festgeschriebene Leseebene . Und wie sich herausstellte, nehmen verschiedene DBMS Isolationsstufen unterschiedlich wahr. Sie können eine Vielzahl von Nuancen bei der Gewährleistung der Isolation aufweisen, zusätzliche Ebenen aufweisen oder nicht bekannt sein.

Erstellen Sie mit dem Docker Hub eine Umgebung mit dem fertigen MySQL-Image. Und füllen Sie die Datenbank.

docker-compose.yaml
version: '3.4' services: db: image: mysql:8 environment: - MYSQL_ROOT_PASSWORD=12345 command: --init-file /init.sql volumes: - data:/var/lib/mysql - ./init.sql:/init.sql expose: - "3306" ports: - "3309:3306" volumes: data: 


Auffüllen der Datenbank
 create database if not exists bank; use bank; create table if not exists accounts ( id int unsigned auto_increment primary key, login varchar(255) not null, balance bigint default 0 not null, created_at timestamp default now() ) collate=utf8mb4_unicode_ci; insert into accounts (login, balance) values ('petya', 1000); insert into accounts (login, balance) values ('vasya', 2000); insert into accounts (login, balance) values ('mark', 500); 


Betrachten wir, wie Ebenen funktionieren und welche Funktionen sie haben.
Wir werden Beispiele für 2 gleichzeitig ausgeführte Transaktionen ausführen. Bedingt wird eine Transaktion im linken Fenster als Transaktion 1 (T1) bezeichnet, im rechten Fenster als Transaktion 2 (T2).

Lesen Sie nicht festgeschrieben


Das Niveau mit der schlechtesten Datenkonsistenz, aber der höchsten Transaktionsgeschwindigkeit. Der Name der Ebene spricht für sich selbst - jede Transaktion sieht nicht festgeschriebene Änderungen in einer anderen Transaktion (das Phänomen des schmutzigen Lesens ). Mal sehen, wie sich solche Transaktionen gegenseitig beeinflussen.

Schritt 1. Wir beginnen 2 parallele Transaktionen.



Schritt 2. Wir schauen uns an, welche Informationen wir am Anfang haben.



Schritt 3. Nun führen wir die Operationen INSERT, DELETE, UPDATE in T1 aus und sehen, was eine andere Transaktion jetzt sieht.



T2 sieht Daten von einer anderen Transaktion, die noch nicht festgeschrieben wurde.

Schritt 4. Und T2 kann einige Daten erhalten.



Schritt 5. Wenn Sie Änderungen an T1 rückgängig machen, sind die von T2 empfangenen Daten fehlerhaft.



Auf dieser Ebene ist es unmöglich, Daten zu verwenden, auf deren Grundlage Schlussfolgerungen und kritische Entscheidungen getroffen werden, die für die Anwendung wichtig sind, da diese Schlussfolgerungen weit von der Realität entfernt sein können.
Diese Ebene kann zum Beispiel für ungefähre Berechnungen von etwas verwendet werden. Das Ergebnis COUNT (*) oder MAX (*) kann in einigen nicht strengen Berichten verwendet werden.
Ein weiteres Beispiel ist der Debug-Modus. Wenn Sie während einer Transaktion sehen möchten, was mit der Datenbank passiert.

Lesen Sie verpflichtet


Auf dieser Ebene werden bei gleichzeitig ausgeführten Transaktionen nur festgeschriebene Änderungen gegenüber anderen Transaktionen angezeigt. Somit bietet diese Stufe Schutz gegen schmutziges Lesen .

Schritt 1 und Schritt 2 ähneln dem vorherigen Beispiel.

Schritt 3. Wir führen auch 3 einfache Operationen mit der Kontentabelle (T1) durch und treffen in beiden Transaktionen eine vollständige Auswahl dieser Tabellen.



Und wir werden sehen, dass das Phänomen des schmutzigen Lesens in T2 fehlt.

Schritt 4. Wir korrigieren die Änderungen in T1 und überprüfen, was T2 jetzt sieht.



Jetzt sieht T2 alles, was T1 getan hat. Dies ist das sogenannte Phänomen des nicht wiederholten Lesens, wenn aktualisierte und gelöschte Zeilen angezeigt werden (UPDATE, DELETE), und das Phänomen des Lesens von Phantomen, wenn hinzugefügte Datensätze angezeigt werden (INSERT).

Wiederholbares Lesen


Eine Stufe, um das Phänomen des nicht wiederholten Lesens zu verhindern. Das heißt, Wir sehen keine geänderten und gelöschten Datensätze in einer anderen Transaktion mit einer anderen Transaktion. Wir sehen aber immer noch die eingefügten Datensätze aus einer anderen Transaktion. Das Lesen von Phantomen geht nirgendwo hin.

Wiederholen Sie Schritt 1 und Schritt 2 erneut.

Schritt 3. In T1 führen wir INSERT-, UPDATE- und DELETE-Abfragen aus. Danach versuchen wir in T2, dieselbe Zeile zu aktualisieren, die in T1 aktualisiert wurde.



Und wir bekommen eine Sperre: T2 wartet, bis T1 Änderungen festschreibt oder zurückrollt.

Schritt 4. Korrigieren Sie die von T1 vorgenommenen Änderungen. Und lesen Sie noch einmal die Daten aus der Konten-Tabelle in T2.



Wie Sie sehen können, werden die Phänomene des nicht wiederholten Lesens und Lesens von Phantomen nicht beobachtet. Wie kommt es, dass wir durch wiederholbares Lesen standardmäßig nur das Phänomen des nicht wiederholten Lesens verhindern können ?

Tatsächlich fehlt MySQL der Effekt des Lesens von Phantomen für die wiederholbare Leseebene . Und in PostgreSQL haben sie es auch für dieses Level losgeworden. Obwohl in der klassischen Darstellung dieser Ebene, müssen wir diesen Effekt beobachten.

Ein kleines abstraktes Beispiel ist der Service zum Generieren von Geschenkgutscheinen (Codes) und deren Verwendung. Ein Angreifer hat beispielsweise einen Zertifikatcode für sich selbst generiert und versucht, ihn zu aktivieren. Dabei wird versucht, mehrere Anforderungen hintereinander zu senden, um einen Gutschein zu aktivieren. In diesem Fall starten wir mehrere gleichzeitig ausgeführte Transaktionen, die mit demselben Coupon arbeiten. In einigen Situationen kann es zu einer doppelten oder sogar dreifachen Aktivierung des Gutscheins kommen (der Benutzer erhält 2x / 3x Boni). Bei wiederholbarem Lesen tritt in diesem Fall eine Sperre auf und die Aktivierung erfolgt einmal. In den vorherigen beiden Ebenen ist eine Mehrfachaktivierung möglich. Ein ähnliches Problem kann auch mit der Abfrage SELECT FOR UPDATE gelöst werden, die auch den aktualisierten Datensatz (Coupon) blockiert.

Serialisierbar


Die Ebene, auf der sich Transaktionen so verhalten, als ob nichts anderes existiert, hat keinen Einfluss aufeinander. In der klassischen Darstellung eliminiert diese Ebene den Effekt des Lesens von Phantomen .

Schritt 1. Starten Sie die Transaktion.

Schritt 2. T2 lesen wir die Konten-Tabelle, dann T1 versuchen wir, die von T2 gelesenen Daten zu aktualisieren.



Wir erhalten eine Sperre: Wir können die Daten in einer Transaktion, die in einer anderen gelesen wurde, nicht ändern.

Schritt 3. Sowohl INSERT als auch DELETE führen uns zur Sperre in T1.



Bis T2 seine Arbeit abgeschlossen hat, können wir nicht mit den gelesenen Daten arbeiten. Wir erhalten maximale Datenkonsistenz, es werden keine zusätzlichen Daten aufgezeichnet. Der Preis dafür ist eine langsame Transaktionsgeschwindigkeit aufgrund häufiger Sperren. Bei einer schlechten Anwendungsarchitektur kann dies einen Streich spielen.

Schlussfolgerungen


In den meisten Anwendungen ändert sich die Isolationsstufe selten und der Standardwert wird verwendet (in MySQL wird er beispielsweise wiederholt gelesen , in PostgreSQL wird er festgeschrieben ).

In regelmäßigen Abständen gibt es jedoch Probleme, bei denen das Finden eines besseren Gleichgewichts zwischen hoher Datenkonsistenz oder Transaktionsgeschwindigkeit zur Lösung eines Anwendungsproblems beitragen kann.

Source: https://habr.com/ru/post/de469415/


All Articles