Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Verstehen des Dirty Read-Problems mit SQL Server

Eines der häufigsten Probleme beim Ausführen gleichzeitiger Transaktionen ist das Dirty Read-Problem. Ein Dirty Read tritt auf, wenn einer Transaktion erlaubt wird, Daten zu lesen, die von einer anderen Transaktion geändert werden, die gleichzeitig läuft, sich aber noch nicht festgeschrieben hat.

Wenn die Transaktion, die die Daten ändert, sich selbst festschreibt, tritt das Dirty-Read-Problem nicht auf. Wenn jedoch die Transaktion, die die Daten ändert, rückgängig gemacht wird, nachdem die andere Transaktion die Daten gelesen hat, enthält die letztere Transaktion schmutzige Daten, die nicht wirklich existieren.

Stellen Sie wie immer sicher, dass Sie gut gesichert sind, bevor Sie mit einem neuen Code experimentieren. Lesen Sie diesen Artikel zum Sichern von MS SQL-Datenbanken, wenn Sie sich nicht sicher sind.

Lassen Sie uns dies anhand eines Beispiels verstehen. Angenommen, wir haben eine Tabelle mit dem Namen „Produkt“, in der die ID, der Name und die Artikel auf Lager für das Produkt gespeichert sind.

Die Tabelle sieht folgendermaßen aus:

[Tabellen-ID=20 /]

Angenommen, Sie haben ein Online-System, in dem ein Benutzer Produkte kaufen und gleichzeitig Produkte anzeigen kann. Sehen Sie sich die folgende Abbildung an.

Stellen Sie sich ein Szenario vor, in dem ein Benutzer versucht, ein Produkt zu kaufen. Transaktion 1 führt die Kaufaufgabe für den Benutzer aus. Der erste Schritt in der Transaktion besteht darin, den Artikelbestand zu aktualisieren.

Vor der Transaktion befinden sich 12 Artikel auf Lager; die Transaktion aktualisiert dies auf 11. Die Transaktion kommuniziert nun mit einem externen Abrechnungs-Gateway.

Wenn zu diesem Zeitpunkt eine andere Transaktion, sagen wir Transaktion 2, ItemsInStock für Laptops liest, lautet sie 11. Wenn sich jedoch später herausstellt, dass der Benutzer hinter Transaktion 1 nicht genügend Guthaben auf seinem Konto hat, wird Transaktion 1 gewürfelt zurück und der Wert für die ItemsInStock-Spalte wird auf 12 zurückgesetzt.

Transaktion 2 hat jedoch 11 als Wert für die Spalte ItemsInStock. Dies sind Dirty Data und das Problem wird Dirty Read Problem genannt.

Arbeitsbeispiel für ein Dirty-Read-Problem

Werfen wir einen Blick auf das Dirty-Read-Problem in Aktion in SQL Server. Lassen Sie uns wie immer zuerst unsere Tabelle erstellen und ihr einige Dummy-Daten hinzufügen. Führen Sie das folgende Skript auf Ihrem Datenbankserver aus.

CREATE DATABASE pos;

USE pos;

CREATE TABLE products
(
	Id INT PRIMARY KEY,
	Name VARCHAR(50) NOT NULL,
	ItemsinStock INT NOT NULL

)

INSERT into products

VALUES 
(1, 'Laptop', 12),
(2, 'iPhone', 15),
(3, 'Tablets', 10)

Öffnen Sie nun zwei SQL Server Management Studio-Instanzen nebeneinander. Wir werden in jedem dieser Fälle eine Transaktion ausführen.

Fügen Sie das folgende Skript zur ersten Instanz von SSMS hinzu.

USE pos;

SELECT * FROM products

-- Transaction 1

BEGIN Tran

UPDATE products set ItemsInStock = 11
WHERE Id = 1

-- Billing the customer
WaitFor Delay '00:00:10'
Rollback Transaction

Im obigen Skript starten wir eine neue Transaktion, die den Wert für die Spalte „ItemsInStock“ der Produkttabelle aktualisiert, wobei die ID 1 ist. Dann simulieren wir die Verzögerung für die Rechnungsstellung an den Kunden, indem wir die Funktionen „WaitFor“ und „Delay“ verwenden. Im Skript wurde eine Verzögerung von 10 Sekunden eingestellt. Danach setzen wir die Transaktion einfach zurück.

In der zweiten Instanz von SSMS fügen wir einfach die folgende SELECT-Anweisung hinzu.

USE pos;

-- Transaction 2

SELECT * FROM products
WHERE Id = 1

Führen Sie nun zuerst die erste Transaktion aus, d. h. führen Sie das Skript in der ersten Instanz von SSMS aus, und führen Sie das Skript dann sofort in der zweiten Instanz von SSMS aus.

Sie werden sehen, dass beide Transaktionen 10 Sekunden lang ausgeführt werden, und danach sehen Sie, dass der Wert für die Spalte „ItemsInStock“ für den Datensatz mit der ID 1 immer noch 12 ist, wie die zweite Transaktion zeigt. Obwohl die erste Transaktion ihn auf 11 aktualisiert, 10 Sekunden gewartet und ihn dann auf 12 zurückgesetzt hat, ist der von der zweiten Transaktion angezeigte Wert 12 und nicht 11.

Was tatsächlich passiert ist, ist, dass bei der Ausführung der ersten Transaktion der Wert für die Spalte „ItemsinStock“ aktualisiert wurde. Es hat dann 10 Sekunden gewartet und die Transaktion dann zurückgesetzt.

Obwohl wir die zweite Transaktion unmittelbar nach der ersten gestartet haben, musste sie warten, bis die erste Transaktion abgeschlossen war. Aus diesem Grund hat die zweite Transaktion auch 10 Sekunden gewartet und die zweite Transaktion unmittelbar nach Abschluss der ersten Transaktion ausgeführt.

Bestätigte Isolationsstufe lesen

Warum musste Transaktion 2 auf den Abschluss von Transaktion 1 warten, bevor sie ausgeführt wurde?

Die Antwort ist, dass die Standard-Isolationsstufe zwischen Transaktionen „Read Committed“ ist. Die Isolationsstufe Read Committed stellt sicher, dass Daten nur dann von einer Transaktion gelesen werden können, wenn sie sich im Committed-Zustand befinden.

In unserem Beispiel hat Transaktion 1 die Daten aktualisiert, sie aber nicht festgeschrieben, bis sie rückgängig gemacht wurden. Aus diesem Grund musste Transaktion 2 darauf warten, dass Transaktion 1 die Daten festschreibt oder die Transaktion rückgängig macht, bevor sie die Daten lesen konnte.

In praktischen Szenarien finden nun häufig mehrere Transaktionen gleichzeitig in einer einzigen Datenbank statt, und wir möchten nicht, dass jede Transaktion warten muss, bis sie an der Reihe ist. Dies kann Datenbanken sehr langsam machen. Stellen Sie sich vor, Sie kaufen etwas online auf einer großen Website, die nur eine Transaktion gleichzeitig verarbeiten kann!

Nicht festgeschriebene Daten lesen

Die Antwort auf dieses Problem besteht darin, Ihren Transaktionen zu erlauben, mit nicht festgeschriebenen Daten zu arbeiten.

Um nicht festgeschriebene Daten zu lesen, setzen Sie einfach die Isolationsstufe der Transaktion auf „Lesen nicht festgeschrieben“. Aktualisieren Sie die Transaktion 2, indem Sie eine Isolationsstufe gemäß dem Skript unten hinzufügen.

USE pos;

-- Transaction 2
set transaction isolation level read uncommitted

SELECT * FROM products
WHERE Id = 1

Wenn Sie nun Transaktion 1 und dann sofort Transaktion 2 ausführen, werden Sie sehen, dass Transaktion 2 nicht darauf wartet, dass Transaktion 1 Daten festschreibt. Transaktion 2 liest sofort die schmutzigen Daten. Dies wird in der folgenden Abbildung dargestellt:

Hier führt die linke Instanz Transaktion 1 und die rechte Instanz Transaktion 2 aus.

Wir führen zuerst Transaktion 1 aus, die den Wert von „ItemsinStock“ für ID 1 auf 11 von 12 aktualisiert und dann 10 Sekunden wartet, bevor sie zurückgesetzt wird.

In der Zwischenzeit liest Transaktion w die schmutzigen Daten, die 11 sind, wie im Ergebnisfenster rechts gezeigt. Da Transaktion 1 zurückgesetzt wird, ist dies nicht der tatsächliche Wert in der Tabelle. Der tatsächliche Wert ist 12. Versuchen Sie erneut, Transaktion 2 auszuführen, und Sie werden sehen, dass diesmal 12 abgerufen wird.

Read uncommitted ist die einzige Isolationsstufe, bei der das Dirty-Read-Problem auftritt. Diese Isolationsstufe ist von allen Isolationsstufen am wenigsten restriktiv und ermöglicht das Lesen nicht festgeschriebener Daten.

Offensichtlich gibt es Vor- und Nachteile bei der Verwendung von Read Uncommitted, es hängt davon ab, für welche Anwendung Ihre Datenbank verwendet wird. Offensichtlich wäre es eine sehr schlechte Idee, dies für die Datenbank hinter Geldautomatensystemen und anderen sehr sicheren Systemen zu verwenden. Für Anwendungen, bei denen Geschwindigkeit sehr wichtig ist (Betreiben großer E-Commerce-Shops), ist die Verwendung von Read Uncommitted jedoch sinnvoller.