1. 概要

このチュートリアルでは、オフライン同時実行制御の概念を紹介し、アプローチの長所と短所、つまり悲観的および楽観的なオフラインロックについて説明します。

2. 動機

アプリケーションを設計するときは、通常、共有データへのある程度の同時アクセスを容易にする必要があります。 これには、データの整合性を保護し、更新の損失や読み取りの不整合などの一般的な問題を回避するための対策が必要です。

失われた更新は、プロセスが最後の取得以降に別のプロセスによって変更されたデータに対して更新を実行するときに発生します。 その後、以前の変更が上書きされます。

一貫性のない読み取りの例は、プロセスが別のプロセスによって更新されている途中のデータを読み取る場合です。 その後、このデータを何らかの意味のある方法で使用すると、問題が発生する可能性があります。

多くの場合、基盤となるリソース–例: データベース–個々のデータベーストランザクションの安全な同時アクセスを処理します。

ただし、そのようなリソースは、個々のデータベーストランザクションの範囲を超えて何も知らないため、データベースとの複数のトランザクションを含むビジネストランザクションを実行する必要がある場合があります。

このような場合、オフライン同時実行制御など、同時実行を処理するためのより高いレベルの戦略を検討する必要があります。

3. オフライン同時実行制御

オフライン同時実行制御は、複数のデータベーストランザクションにまたがる可能性のあるビジネストランザクションのデータへの同時アクセスを処理する戦略です。

本日は、楽観的および悲観的なオフラインロックという2つのアプローチを紹介します。

4. 楽観的なオフラインロック

4.1. それは何ですか?

楽観的なオフラインロックは、2つの同時ビジネストランザクション間に競合が発生したことを検出し、それに応じてロールバックを実行する手法です。

基本的に、トランザクションをコミットする前に、ビジネストランザクションに最新バージョンのデータがあるかどうかをチェックします。 そうでない場合は、トランザクションを破棄し、この時点までの変更をロールバックします。

これにより、更新が失われる問題が回避されます。このデータが最後の取得以降に変更された場合、ビジネストランザクションがデータを更新することは許可されません。

ただし、読み取りの不整合の問題は解決されません。また、競合が発生するたびに、放棄されたプロセスがその作業を失うことも意味します。 これは、競合が頻繁に発生することが予想される場合、またはビジネストランザクションを放棄することの影響が大きい場合には理想的ではありません。

4.2. 実装

一般的なアプローチは、バージョン番号をデータベース内の各レコードに関連付けることです。 次に、レコードを読み取るときに、そのバージョンをそのバージョンと一緒に、たとえばセッション状態で保存します。

変更が行われた後、レコードをデータベースに書き戻す前に、データベースからバージョン番号を再度取得し、それが現在のバージョンと一致するかどうかを確認します。

もしそうなら、データを安全に書き戻すことができます。 更新する行のロックを取得し、新しいデータをデータベースに書き込み、変更された各レコードのバージョン番号を更新するようにします。

そうでない場合は、データが古くなっている必要があります。前回の取得以降、別のプロセスによってデータが変更されています。 したがって、トランザクションを失敗させるだけです。

このプロセスを視覚的に表したものは次のとおりです。

追加の詳細を取得するには、JPAとHibernateを使用したオプティミスティックオフラインロックの実装例を確認してください。

4.3. いつ使うべきですか?

これまで見てきたように、このアプローチは実装がかなり簡単です。

また、良好な活気を促進します。コミット中はレコードが短時間ロックされますが、それ以外の場合、プロセスは同時にデータに自由にアクセスできます。

ただし、ここでのトレードオフは、競合を検出するたびに、古いデータに基づいて動作するビジネストランザクションは、その時点までの作業を失うことです。 競合は11時間目にのみ検出されるため、これはユーザーに大きなフラストレーションを引き起こす可能性があります。

したがって、システムの活気と実装の容易さを優先する場合は、この手法を使用する必要があります。

ビジネストランザクションに、繰り返す必要のない多くのステップが含まれている場合は、この手法を避ける必要があります。

5. 悲観的なオフラインロック

5.1. それは何ですか?

ここでの基本的な考え方は、各プロセスがビジネストランザクションの開始時に共有データのロックを取得する必要があるということです。 したがって、トランザクションが終了するまで待機して競合が発生したと判断するのではなく、そもそも競合が発生しないようにします。

1人のユーザーが一部の共有データにアクセスするビジネストランザクションを開始した場合、他のユーザーは同じアクションを試みることができません。 つまり、データベースにデータを書き戻すときは、競合が発生しないことがわかっているため、誰も作業を失うことはありません。

5.2. 実装

この戦略は、楽観的な戦略よりもかなり複雑です。 それを実装する前に、いくつかの決定を行う必要があります。

まず、使用するロックの種類を決定する必要があります。

  1. 排他的読み取りロック–プロセスは、データを読み取ったり変更したりするために単一のロックを取得します。
  2. 排他的書き込みロック–プロセスはデータを自由に読み取ることができますが、データを変更するには単一のロックを取得する必要があります。
  3. 読み取り/書き込みロック–いつでも、複数のプロセスがデータの読み取りロックを取得したり、1つのプロセスが書き込みロックを取得したりできます。

また、ロックマネージャも必要です。ロックの取得と解放を処理し、どのプロセスがどのロックを持っているかを追跡するメカニズムです。 これは、サーバー側のデータ構造や追加のデータベーステーブルなど、さまざまな形式をとることがあります。

この手法の使用例を次に示します。

この手法の動作を確認するには、JPAとHibernateを使用したペシミスティックロックを確認してください。

5.3. いつ使うべきですか?

失われた作業を回避することは、このアプローチの1つの大きな利点です。 ビジネストランザクションに複数のステップが必要な場合、トランザクションの終了時ではなく開始時にプロセスに失敗すると、ユーザーのフラストレーションを大幅に軽減できます。 悲観的なオフラインロックは、「読み取り」または「読み取り/書き込み」ロックが選択されている場合に、更新の損失を防ぎ、一貫性のない読み取りから保護します。

ただし、この戦略の重大な欠点は、活気への影響です。 当然、リソースへのアクセスを排他的にすることで、一部のユーザーを強制的に待機させます。 ユーザーが制限されたリソースに頻繁にアクセスする場合、これはシステムの目標に悪影響を与える可能性があります。

また、これまで見てきたように、悲観的なオフラインロックは実装が複雑になる可能性があります。

したがって、プロセス間の競合が頻繁に発生する可能性がある場合、またはこれらの競合のコストが高い場合にのみ、価値がある可能性があります。

6. 結論

本日、複数のデータベーストランザクション間での同時データアクセスを処理するための便利なツールを紹介しました。 楽観的なオフラインロックは一般的にデータの整合性よりもシステム内の活気を優先し、悲観的なオフラインロックはその逆を優先する方法について説明しました。