PR

2相ロッキングプロトコル /2PL とは?3分でわかりやすく解説

Database

2相ロッキングプロトコル(2PL/2相ロック)とは、データベース管理システム(DBMS)における並行制御のための一般的なプロトコルです。(参考 データベース管理システムとは?

2相ロッキングプロトコル(2PL/2相ロック)
図1:2相ロッキングプロトコル(2PL/2相ロック)

このプロトコルに従うトランザクションは、ロックの取得が成長フェーズ縮小フェーズの2つの段階で行われるため、この名前が付けられました。

成長フェーズではロックを取得し、縮小フェーズではロックを解放します。結果として、2PLはデータベースシステムにおける一貫性と整合性を確保する重要な役割を果たします。

このページでは、2相ロッキングプロトコル(2PL)の基本原則から具体的な動作、利点と欠点、実装例までを総合的に解説します。データベースエンジニアを目指す方であれば知らないと恥ずかしい基本知識の1つです。是非最後までご覧ください。

スポンサーリンク

2相ロッキングプロトコル(2PL)とは?

2相ロッキングプロトコル(2PL)は、名前の通り、2つのフェーズで構成されるロッキングプロトコルです。データベースで多くの人が同時に作業するとき、お互いの作業がぶつからないように管理する方法の1つとして使用されます。

2相ロッキングプロトコル(2PL)は、具体的には成長フェーズ縮小フェーズの2つのフェーズから成り立ちます。

2相ロッキングプロトコル(2PL/2相ロック)
図1:2相ロッキングプロトコル(2PL/2相ロック)

PL1 成長フェーズ(Growing Phase)

  • 目的: トランザクションが必要なリソースに対してロックを取得します。
  • 動作: このフェーズでは、トランザクションは新たなロックを取得することができますが、一度解放したロックを再取得することはできません。
  • 注意点: 成長フェーズの終了時点で必要な全てのロックを取得しておく必要があります。

PL2 縮小フェーズ(Shrinking Phase)

  • 目的: トランザクションが完了するために不要なロックを解放します。
  • 動作: このフェーズでは、トランザクションはロックを解放することができますが、新たなロックを取得することはできません。
  • 注意点: 必要なロックを解放してもデータの整合性が保たれるように慎重に解放する必要があります。
なぜ2つのフェーズに分けるのか?

2相ロッキングプロトコルが2つのフェーズに分かれる理由は、データの一貫性と整合性を保つためです。

具体例を通して、この点を詳しく見ていきましょう。

銀行の口座間送金

口座Aから口座Bへの送金を行うトランザクションを考えます。

ステップ1 成長フェーズ

  • ロック取得: 口座Aの残高をチェックし、送金額が足りるか確認するため、口座Aにロックを取得。
  • 新たなロック取得: 送金額が足りることが確認できれば、送金先の口座Bにロックを取得。

この段階では、ロックの取得が柔軟に行えます。

ステップ2 縮小フェーズ

  • ロック解放の開始: 口座Aから送金額を減らし、口座Bに送金額を加算後、ロックを解放。
  • 新しいロック取得の禁止: 一度縮小フェーズに入ると新しいロックの取得はできなくなります。

もし、この操作を2相ロッキングプロトコルに従わないとすると、例えばトランザクション1が口座Aのロックを取得している間に、トランザクション2が口座Bのロックを取得してしまう可能性があります。両者が互いの口座にロックを取得しようとすると、お互いが待ち合いの状態に陥りデッドロックが発生します。

また、同様に送金処理中に他のトランザクションが同じ口座への入金を行うと、最終的な口座残高が正しく反映されない可能性が出てきてしまいます。

2相ロッキングプロトコルは、データベースの一貫性と整合性を確保する重要なプロセス。2つのフェーズに分けない場合、死ロックの発生、データの不整合、パフォーマンスの低下など、多岐にわたる弊害が生じる可能性が生じてしまいます。

ざっくりいうと、2相ロッキングプロトコルは「まずは必要なデータすべてに鍵をかけ、安全な状態にしてから処理を始め、その後、順番に鍵を外していく」というルールです。これにより、同時に複数の処理が行われても、データが混ざり合って矛盾が生じるのを防ぐことができる、というわけです。

2相ロッキングプロトコルのロックの種類

2相ロッキングプロトコルでは、以下のロックが組み合わせて使用され、データベースの一貫性と整合性を確保します。各ロックの種類とその特性を理解することは、効果的なトランザクション処理の設計において重要なので、ここで詳細を以下に記載しておきます。

ロックの種類概要用途特徴
共有ロック(Shared Lock)複数のトランザクションがデータを読むことを許可する読み取り専用の処理他の共有ロックは取得可、排他ロックは取得不可
排他ロック(Exclusive Lock)単一のトランザクションだけがデータにアクセスできる書き込みを伴う処理(更新、削除など)他のいかなる種類のロックも取得不可
意図ロック(Intention Lock)特定のロックを取得する意図を表明する意図の通知デッドロック防止の効率的な手段
互換性のあるロック2つの異なるトランザクションが同時に取得できる同時データアクセス例:共有ロック同士は互換性があるため、同時に取得可
2相ロッキングプロトコルで使用される主要なロックの

2相ロッキングプロトコルのメリット・デメリット

ここからは、2相ロッキングプロトコル(2PL)のメリットとデメリットについて、より具体的に実際のコード例も交えながらご説明していきます。

メリット1:整合性の保証

2PLはデータへのアクセスが他のトランザクションと干渉しないように制御します。これにより、データの整合性、つまり一貫性が保たれます。

以下のコードは、トランザクションを開始する際に、必要なロックを取得するシンプルな例です。

def start_transaction():
    acquire_locks() # データにアクセスする前にロックを取得
    # 以下、処理の実行

ロックを取得することで、同時にデータへのアクセスを試みる他のトランザクションと競合しないようにします。

メリット2:並行処理のサポート

2PLは、多くのトランザクションを同時に効率的に処理できます。ロックの管理により、複数のトランザクションが同時にデータにアクセスしても問題が発生しないようにします。

# 複数のトランザクションを並行実行
threads = [Thread(target=transaction_process) for _ in range(10)]
for t in threads:
    t.start()

このコードは、10個のトランザクションを同時に起動しています。それぞれのトランザクションは独立して処理されます。

デメリット1:デッドロックの可能性

デッドロックは、トランザクション同士が互いのロックを待ち合う状態のことです。2PLではこの問題が発生する可能性があります。

# デッドロックの検知と解決
def detect_deadlock():
    # デッドロックの検知ロジック
    # 必要に応じてロックの解放

このコード例は、デッドロックを検知し、必要に応じてロックを解放する仕組みを示しています。

関連 デッドロックとは?

デッドロックとは、複数のプログラムやトランザクションが同時に実行されるときに、お互いが必要とするリソース(例えばデータやファイル、メモリなど)を既に相手が保持しているため、どちらも先に進めなくなってしまう状態を指します。

たとえば、プロセスAがリソースXを持っていてリソースYを要求し、同時にプロセスBがリソースYを保持してリソースXを必要とすると、どちらも相手のリソースが解放されるのを待つだけになり、結果として永遠に動けなくなってしまうのです。

これは、狭い通路でお互いに譲らず前に進めない二人の人の状況に例えることができます。

この現象が発生すると、システム全体が停止してしまう可能性があるため、特にデータベースや複雑なマルチタスク環境では大きな問題となります。システムの設計段階から、どのリソースをどの順番で確保するか、または一定時間内に取得できなかった場合はタイムアウトして処理を中断するなどの工夫をすることで、デッドロックの発生リスクを軽減できます。

デメリット2:パフォーマンスの低下

2PLでロックの競合が頻繁に発生すると、システムの効率が低下することがあります。つまる、あるトランザクションがロックを持っている間、他のトランザクションが待機する必要があるため、処理の遅延が発生する可能性があります。

from threading import Lock

lock = Lock()

def transaction_process():
    with lock: # このロックが競合すると効率が低下します
        # 複雑な処理
        pass

# 複数のトランザクションを並行実行
threads = [Thread(target=transaction_process) for _ in range(10)]
for t in threads:
    t.start()

このコードは、10個のトランザクションが同一のロックを取得しようとして競合する状況を示しています。一度に1つのトランザクションしかロックを取得できないため、他のトランザクションは待機する必要があります。この待機時間が積み重なると、システム全体の効率が低下する可能性があります。

この問題を解決するためには、ロックの粒度を調整する、トランザクションのスケジューリングを工夫するなどの工夫が必要です。

  1. 定義: データベーストランザクションにおける並行処理を管理するプロトコル。
  2. 構成:
    • ロック取得フェーズ: 必要なロックを取得し、データにアクセスします。
    • ロック解放フェーズ: 作業完了後、ロックを解放します。
  3. 利点:
    • 整合性保証: トランザクション間でのデータ整合性を保つ。
    • 並行処理: 複数のトランザクションの同時実行をサポート。
  4. 欠点:
    • デッドロック: ロックの相互待ちによる停止の可能性。
    • 効率の問題: ロック競合による処理の遅延。

補足:よくある質問

Q
概念は理解したが、結局2PLって実装方式のこと?
A

2PL(2相ロッキング)は 実装というよりはアルゴリズム(制御方式) です。
データベース管理システム(DBMS)などがトランザクションの整合性を確保するために採用する ロック制御のルール の一種です。つまり・・・・

DBMSの開発者がこの2PLのルールに従ってロックを管理するように 実装 していく、ということ。
2PLは「概念」や「ルール」であり、それをシステムの中で「実装するかどうか」はDBMS次第です。

代表的なDBMSでの実装例

  • MySQL(InnoDB):トランザクション分離レベルによっては2PLベースのロック管理を使用
  • PostgreSQL:2PLではなくMVCC(多版同時実行制御)を主に採用
  • Oracle DB:基本的にはMVCCを用いるが、必要に応じてロック制御を組み合わせる
タイトルとURLをコピーしました