DBMS - Deadlock: Understanding and Preventing Database Bottlenecks

Hallo, ambitionierte Datenbankenthusiasten! Ich freue mich sehr, euch durch die faszinierende Welt der Datenbankmanagementsysteme (DBMS) und eines seiner kniffligsten Konzepte zu führen: Deadlocks. Keine Sorge, wenn ihr neu im Programmieren seid; wir beginnen bei den Grundlagen und arbeiten uns hinauf. Am Ende dieses Tutorials werdet ihr Detektive für Deadlocks sein, in der Lage, diese lästigen Datenbankengpässe zu erkennen und zu verhindern!

DBMS - Deadlock

Was ist ein Deadlock?

Stellt euch vor, ihr und euer Freund sitzt an einem Tisch mit nur einer Gabel und einem Messer. Ihr braucht beides, um zu essen, aber ihr haltet jeweils eines und weigert euch, loszulassen, bis ihr das andere bekommt. Das ist im Grunde genommen, was ein Deadlock in der Datenbankwelt ist!

In DBMS-Begriffen tritt ein Deadlock auf, wenn zwei oder mehr Transaktionen auf Ressourcen warten, die sie halten. Es ist wie ein digitales mexikanisches Stand-off, bei dem niemand vorankommt.

Schauen wir uns ein einfaches Beispiel an:

-- Transaktion 1
BEGIN TRANSACTION;
UPDATE Accounts SET Balance = Balance - 100 WHERE AccountID = 1;
UPDATE Accounts SET Balance = Balance + 100 WHERE AccountID = 2;
COMMIT;

-- Transaktion 2
BEGIN TRANSACTION;
UPDATE Accounts SET Balance = Balance - 50 WHERE AccountID = 2;
UPDATE Accounts SET Balance = Balance + 50 WHERE AccountID = 1;
COMMIT;

In diesem Szenario könnte Transaktion 1 Konto 1 und Transaktion 2 Konto 2 gleichzeitig aktualisieren, und sie könnten endlos aufeinander warten, was zu einem Deadlock führt.

Deadlock-Verhinderung

Nun, da wir verstehen, was ein Deadlock ist, lassen uns herausfinden, wie man ihn verhindert. Die Verhinderung von Deadlocks involves das System so zu strukturieren, dass die Möglichkeit von Deadlocks ausgeschlossen wird.

1. Ressourcenreihenfolge

Eine effektive Methode ist es, immer Ressourcen in einer bestimmten Reihenfolge anzufordern. Es ist wie das Sagen unseren Essern, immer zuerst die Gabel und dann das Messer zu nehmen. Auf diese Weise werden sie nie in einer Situation sein, in der sie das jeweils andere benötigte Besteck halten.

Hier ist, wie wir unser vorheriges Beispiel umschreiben könnten, um Deadlocks zu verhindern:

-- Transaktion 1
BEGIN TRANSACTION;
UPDATE Accounts SET Balance = Balance - 100 WHERE AccountID = 1;
UPDATE Accounts SET Balance = Balance + 100 WHERE AccountID = 2;
COMMIT;

-- Transaktion 2
BEGIN TRANSACTION;
UPDATE Accounts SET Balance = Balance - 50 WHERE AccountID = 1;
UPDATE Accounts SET Balance = Balance + 50 WHERE AccountID = 2;
COMMIT;

Durch das ständige Aktualisieren von Konto 1 vor Konto 2 stellen wir sicher, dass die Transaktionen nicht in einen Deadlock geraten.

2. Sperrzeitüberschreitungen

Eine andere Verhinderungsmethode ist die Verwendung von Sperrzeitüberschreitungen. Es ist wie das Sagen unseren Essern: "Wenn ihr nicht beide Bestecke in 5 Minuten bekommt, gebt auf und probiert es später noch einmal."

In SQL Server könnt ihr eine Sperrzeit wie folgt einstellen:

SET LOCK_TIMEOUT 5000; -- Sperrzeit auf 5 Sekunden setzen

Auf diese Weise wird eine Transaktion automatisch rückgängig gemacht, wenn sie innerhalb von 5 Sekunden keine Sperre erwerben kann, was einen möglichen Deadlock verhindert.

3. Verringerung der Sperrdauer

Je weniger Zeit Ressourcen gesperrt sind, desto geringer ist die Wahrscheinlichkeit eines Deadlocks. Es ist wie das Sagen unseren Essern, schneller zu essen! In Datenbankterminologie bedeutet dies, Transaktionen so kurz wie möglich zu halten.

Hier ist ein Beispiel, wie man die Sperrdauer verringern kann:

-- Anstatt dies:
BEGIN TRANSACTION;
-- Führt einige lange Verarbeitungsvorgänge durch
UPDATE Accounts SET Balance = Balance - 100 WHERE AccountID = 1;
COMMIT;

-- Tut dies:
-- Führt einige lange Verarbeitungsvorgänge durch
BEGIN TRANSACTION;
UPDATE Accounts SET Balance = Balance - 100 WHERE AccountID = 1;
COMMIT;

Durch das Verschieben der langen Verarbeitungsvorgänge außerhalb der Transaktion verringern wir die Zeit, in der die Sperre gehalten wird.

Deadlock-Vermeidung

Während die Verhinderung darauf abzielt, Deadlocks unmöglich zu machen, geht es bei der Vermeidung darum, intelligent decisions während der Laufzeit zu treffen, um mögliche Deadlocks zu umgehen.

1. Wait-Die-Schema

In diesem Schema werden älteren Transaktionen Vorrang eingeräumt. Wenn eine jüngere Transaktion eine von einer älteren gehaltene Ressource anfordert, "stirbt" sie (wird rückgängig gemacht) und startet neu. Wenn eine ältere Transaktion eine von einer jüngeren gehaltene Ressource anfordert, wartet sie.

Hier ist eine Pseudocode-Darstellung:

def request_resource(transaction, resource):
if resource.is_held_by_younger_transaction(transaction):
wait(transaction, resource)
else:
die_and_restart(transaction)

2. Wound-Wait-Schema

Dies ist das Gegenteil von Wait-Die. Ältere Transaktionen "verletzen" (machen rückgängig) jüngere, und jüngere Transaktionen warten auf ältere.

def request_resource(transaction, resource):
if resource.is_held_by_younger_transaction(transaction):
wound(resource.holder)
else:
wait(transaction, resource)

3. Bankiersalgorithmus

Dieser clevere Algorithmus, benannt nach den Kreditpraktiken von Banken, entscheidet, ob die Gewährung einer Ressourcenanfrage zu einem Deadlock führen könnte. Wenn ja, wird die Anfrage abgelehnt.

Hier ist eine vereinfachte Version des Bankiersalgorithmus:

def is_safe_state(available, max_need, allocation):
work = available.copy()
finish = [False] * len(allocation)

while True:
found = False
for i in range(len(allocation)):
if not finish[i] and (max_need[i] - allocation[i] <= work).all():
work += allocation[i]
finish[i] = True
found = True

if not found:
break

return all(finish)

def request_resources(process_id, request):
if request > max_need[process_id] - allocation[process_id]:
return False  # Anfrage übersteigt maximale Forderung

if request > available:
return False  # Ressourcen nicht verfügbar

# Temporär Ressourcen zuweisen
available -= request
allocation[process_id] += request

if is_safe_state(available, max_need, allocation):
return True  # Anfrage gewähren
else:
# Änderungen rückgängig machen und Anfrage ablehnen
available += request
allocation[process_id] -= request
return False

Dieser Algorithmus überprüft, ob die Gewährung einer Anfrage das System in einen sicheren Zustand versetzen würde, in dem alle Prozesse beenden können. Wenn nicht, wird die Anfrage abgelehnt.

Schlussfolgerung

Herzlichen Glückwunsch! Ihr habt gerade eure ersten Schritte in die Welt der Deadlock-Verhinderung und -Vermeidung unternommen. Denkt daran, dass das Umgang mit Deadlocks wie das Arbeiten als Verkehrspolizist an einer belebten Kreuzung ist. Manchmal muss man Probleme im Voraus verhindern (wie das Aufstellen von Ampeln), und manchmal muss man schnelle Entscheidungen treffen, um Unfälle zu vermeiden (wie das manuelle Lenken des Verkehrs).

Während eurer Reise im Datenbankmanagement werdet ihr auf komplexere Szenarien treffen, aber die Prinzipien, die wir hier besprochen haben, werden als solide Grundlage dienen. Übt weiter, bleibt neugierig undchtet nicht davor, zu experimentieren. Schließlich hat jeder großartige Datenbankadministrator genau dort angefangen, wo ihr jetzt seid!

Frohes Coden und mögen eure Transaktionen stets deadlock-frei sein!

Credits: Image by storyset