DBMS - デッドロック: 理解とデータベースボトルネックの防止

こんにちは、データベースの熱心な愛好家たち!データベース管理システム(DBMS)の魅力的な世界と、その中でも特に難しい概念であるデッドロックについて案内していくことに兴奋しています。プログラミングの初心者の方もお気遣いなく、基本的なところから始めて少しずつ進めていきます。このチュートリアルの終わりには、デッドロックの探偵となり、これらの厄介なデータベースボトルネックを見つけて防止できるようになるでしょう!

DBMS - Deadlock

デッドロックとは?

あなたとあなたの友達が、一つだけのフォークとナイフがあるテーブルに座っていると imagine してください。あなたたちはどちらも食事をするためにこの二つの道具を必要としますが、片方ずつを手に持ち、他の片方を手放すことを拒否しています。これがデータベースの世界でのデッドロックです!

DBMSの用語では、デッドロックは二つ以上のトランザクションが、お互いが保持しているリソースを解放するのを待っている状態です。まるでデジタルのメキシコスタンドオフのように、誰も進むことができません。

簡単な例を見てみましょう:

-- トランザクション1
BEGIN TRANSACTION;
UPDATE Accounts SET Balance = Balance - 100 WHERE AccountID = 1;
UPDATE Accounts SET Balance = Balance + 100 WHERE AccountID = 2;
COMMIT;

-- トランザクション2
BEGIN TRANSACTION;
UPDATE Accounts SET Balance = Balance - 50 WHERE AccountID = 2;
UPDATE Accounts SET Balance = Balance + 50 WHERE AccountID = 1;
COMMIT;

このシナリオでは、トランザクション1がアカウント1を更新し、トランザクション2がアカウント2を更新するυτόと同時に発生すると、お互いが無限に待ち続けることになり、デッドロックが発生します。

デッドロックの防止

デッドロックが何かを理解したので、次にそれを防止する方法を見てみましょう。デッドロックの防止は、デッドロックが発生する可能性を排除するようにシステムを構築することを意味します。

1. リソースの順序

効果的な方法の一つは、常に特定の順序でリソースを要求することです。これは、 dinerたちに「まずフォークを持ち、その後ナイフを持ち」るように指示するのに似ています。このようにすることで、彼らは決してお互いが必要とする道具を持っている状態にはなりません。

以下に、デッドロックを防止するために前の例を書き換えたものを示します:

-- トランザクション1
BEGIN TRANSACTION;
UPDATE Accounts SET Balance = Balance - 100 WHERE AccountID = 1;
UPDATE Accounts SET Balance = Balance + 100 WHERE AccountID = 2;
COMMIT;

-- トランザクション2
BEGIN TRANSACTION;
UPDATE Accounts SET Balance = Balance - 50 WHERE AccountID = 1;
UPDATE Accounts SET Balance = Balance + 50 WHERE AccountID = 2;
COMMIT;

アカウント1を更新する前にアカウント2を更新することで、トランザクションがデッドロックしないようになります。

2. ロックタイムアウト

もう一つの防止方法は、ロックタイムアウトを使用することです。これは、 dinerたちに「5分以内に両方の道具を手に入れられない場合は、諦めて後で再試行する」と言うのに似ています。

SQL Serverでは、以下のようにロックタイムアウトを設定できます:

SET LOCK_TIMEOUT 5000; -- タイムアウトを5秒に設定

このようにすることで、トランザクションが5秒以内にロックを取得できない場合、自動的にロールバックされ、デッドロックが発生する可能性を防止します。

3. ロックの期間を短くする

リソースがロックされている時間が短いほど、デッドロックの可能性が低くなります。これは、 dinerたちに「早く食事を終わらせる」と言うのに似ています。データベースの用語では、これはトランザクションをできるだけ短くすることを意味します。

以下に、ロック期間を短くする例を示します:

-- このようにする代わりに:
BEGIN TRANSACTION;
-- 長い処理を行う
UPDATE Accounts SET Balance = Balance - 100 WHERE AccountID = 1;
COMMIT;

-- このようにする:
-- 長い処理を行う
BEGIN TRANSACTION;
UPDATE Accounts SET Balance = Balance - 100 WHERE AccountID = 1;
COMMIT;

長い処理をトランザクションの外に移動することで、ロックが保持されている時間を短くします。

デッドロックの回避

防止はデッドロックを不可能にすることを目的とするのに対して、回避は実行時にお互いのデッドロックの可能性を避けるための賢明な決定を行うことです。

1. ウェイト・ダイ・スキーム

このスキームでは、古いトランザクションに優先権を与えます。より若いトランザクションが古いトランザクションが保持しているリソースを要求すると、「ダイ」(ロールバック)して再起動します。古いトランザクションが若いトランザクションが保持しているリソースを要求すると、待機します。

以下に pseudocode の表現を示します:

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

2. ウンド・ウェイト・スキーム

これはウェイト・ダイの逆で、古いトランザクションが若いトランザクションを「ウンド」(ロールバック)し、若いトランザクションは古いトランザクションを待機します。

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

3. バンカーのアルゴリズム

この賢いアルゴリズムは、銀行のローン実践に基づいており、リソース要求を許可するとデッドロックに至るかどうかを決定します。それがそうである場合、要求は拒否されます。

以下にバンカーのアルゴリズムの簡易版を示します:

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  # 要求が最大の申込みを超える

if request > available:
return False  # 利用可能なリソースがない

# 一時的にリソースを割り当てる
available -= request
allocation[process_id] += request

if is_safe_state(available, max_need, allocation):
return True  # 要求を許可
else:
# 変更を戻し、要求を拒否
available += request
allocation[process_id] -= request
return False

このアルゴリズムは、要求を許可するとシステムが安全な状態になるかどうかを確認します。それがそうでない場合、要求を拒否します。

結論

おめでとうございます!デッドロックの防止と回避の世界への最初の一歩を踏み出しました。デッドロックを扱うことは、交通量の多い交差点で交通警察官になるのに似ています。時には問題が発生する前に予防措置を講じる(交通信号を設置する)ことも必要であり、時には事故を避けるために迅速な判断を下す(交通を手で誘導する)ことも必要です。

データベース管理の旅を続ける中で、より複雑なシナリオに直面することもありましょうが、ここで説明した原則はsolidな基盤となります。続けて練習し、好奇心を持ち、実験を恐れずにください。毕竟、優れたデータベース管理者も皆、あなたと同じ場所から始まっています!

ハッピーコーディング、そしてあなたのトランザクションが常にデッドロックフリーでありますように!

Credits: Image by storyset