ACID プロパティは、データベース理論の基礎であり、理論的に信頼できるデータベースが持つ必要がある 4 つのプロパティ (原子性、一貫性、分離性、耐久性) を定義します。 4 つのプロパティはすべて重要ですが、分離が最も柔軟です。ほとんどのデータベースには分離レベルの選択肢が用意されており、多くのライブラリでは、よりきめの細かい分離を作成するために追加のレイヤーが追加されています。分離レベルがこれほど広く使用されているのは、主に分離制約を緩和するとスケーラビリティとパフォーマンスが桁違いに向上することが多いためです。
シリアル整合性は、利用可能な最も古く、最も高度な分離レベルの 1 つであり、これが提供する単純なプログラミング モデルにより、一度に 1 つのトランザクションのみを実行できるため、多くの潜在的なリソースの問題が回避されます。それにもかかわらず、ほとんどのアプリケーション (特に Web アプリケーション) は、エンド ユーザーの観点からは非現実的であるため、この非常に高いレベルの分離を採用していません。大規模なユーザー ベースを持つアプリケーションでは、共有リソースにアクセスするときに数分の遅延が発生します。 、ユーザーの数がすぐに減少する可能性があります。弱い整合性と結果整合性は、Web などの大規模な分散データ ソースでは広く見られます。いくつかの成功した大規模 Web アプリケーション (eBay や Amazon など) は、楽観的な弱一貫性の方が従来の悲観的なメカニズムよりもはるかに優れたスケールで対応できることを示しています。この記事では、8 つの異なる分離レベルについて説明します。データ整合性の制約を適切に緩和する方法を学習すると、アプリケーションでこれらの 8 つの分離レベルを使用して、より優れたパフォーマンスとスケーラビリティを実現できます。
同時実行制御の主な目的は、トランザクションが分離され、他のトランザクションに影響を与えないようにすることです。高レベルの分離を達成するには、パフォーマンスが犠牲になります。同時実行制御は、悲観的メカニズムまたは楽観的メカニズムを使用して実装できます。ほとんどのリレーショナル データベースは、書き込みの最適化を実現するために悲観的なメカニズムを使用します。悲観的なメカニズムではロックが使用され、一部の操作をブロックしたり、何らかの形式の競合検出を実行したりできます。テーブル、ページ、または行が変更されると、ペシミスティック メカニズムのロックを使用して、変更されたリソースにアクセスする可能性のある他のトランザクションをブロックできます。ただし、オプティミスティック メカニズムではロックは使用されず、競合検出のみに依存してトランザクションの分離が維持されます。オプティミスティック メカニズムによって採用された競合検出により、すべての読み取り操作が許可され、トランザクションの終了時にその一貫性がチェックされます。競合が検出された場合、トランザクションはロールバックまたは再実行されます。ほとんどの Web サーバーは読み取りに最適化されているため、オプティミスティック メカニズムを使用します。すべての読み取り操作を許可することにより、オプティミスティック メカニズムは高い読み取りおよび書き込みスループットを保証するだけでなく、リソースが常に変更されない場合でもデータの一貫性を保証します。
以下にリストされている分離レベルは、Web 開発者がプログラミング モデルに課された制約をより深く理解し、必要なデータの整合性を維持しながら最も効果的な分離レベルを選択する方法をシステム アーキテクトと開発者が議論できるように設計されています。これらは、最も分離されていない (コミットされていない読み取り) から最も分離されている (シリアル化可能) までリストされています。
非コミット読み取り分離レベルでは、トランザクション間の分離はほとんど必要ありません。すべての読み取り操作では、トランザクション内の保留中の書き込み操作 (ダーティ読み取り) を確認できます。ただし、ダーティ書き込みを防ぐために、コミットされた書き込みにはシリアル順序が必要です。悲観的なメカニズムは、他の書き込みがコミットされるかロールバックされるまで、競合する書き込みをブロックします。オプティミスティック メカニズムはこれらの操作をロックせず、すべての操作を通過させます。接続がロールバックされると、同じデータを変更する後続の他の操作もロールバックされます。このレベルでは、共有バッファは検証なしで使用できます。この分離レベルは、トランザクションが必要ない場合 (読み取り専用データ セットなど)、またはトランザクションが排他的な場合にのみデータベースを変更する場合に使用するのが最適です。
例: オフラインでのみ更新されるアーカイブ データベース、またはトランザクションで使用されない監査/ログ テーブル。
コミットされた読み取りは、システム内の任意のコミットされたステータスを読み取ることができ、現在の接続で発生した変更を結果に反映できる限り、検証なしでバッファリングすることができます (混合ステータス)。悲観的なメカニズムは、これを単調なビューとして実装します。オプティミスティック トランザクションは、すべての変更を分離して保存するため、コミットされるまでは使用できません。 Read Committed は、トランザクションがコミットされるまですべての変更の書き込みを延期する非常に楽観的なメカニズムを使用します。この形式の楽観的分離により、読み取り操作をブロックすることなく複雑な書き込み操作が可能になり、検証モードはありません。共有バッファはコミットされた状態でのみ使用できます。この分離レベルは、結果で古い値が使用でき、トランザクションが書き込み操作にのみ使用できる場合に最適に使用されます。
例: 最新の現在の投稿を表示する必要がなく、投稿間のデータが競合しないオンライン フォーラム。
単調ビューはコミットされた読み取りの拡張であり、トランザクションは実行時にデータベース内の単調増加状態を観察します。このレベルでは、明らかな書き込みトランザクションがある場合、悲観的なトランザクションは読み取り操作をブロックします。オプティミスティック トランザクションは、読み取りコミットされているかのように動作し、すべての変更を分離して保存し、バッファーを検証してそれらがまだ有効であることを確認します。このレベルはデータベース コピーを定期的に同期し、トランザクションが不要な場合、または書き込みトランザクションのみである場合に最適です。
例: 1 人のみが変更できるユーザー設定テーブル。
スナップショット読み取りは単調ビューを拡張し、クエリ結果がデータベースの一貫したスナップショットに確実に反映されるようにします。悲観的なメカニズムは、読み取り操作中の結果に影響を与える他の書き込み操作をブロックします。オプティミスティック メカニズムでは、他の書き込み操作が許可され、トランザクションの一部が変更されてロールバックされたことが読み取りトランザクションに通知されます。オプティミスティックなメカニズムを実装するには、読み取り操作が終了する前に、並列書き込み操作によって結果が変更されているかどうかを確認する必要があります。変更されている場合は、結果がやり直しまたはロールバックされる可能性があります。この検証プロセスでは、同じテーブルで書き込み操作が発生したかどうかを単にチェックすることも、変更されたクエリ結果をチェックすることもできます。オプティミスティック分離レベルでは、競合を簡単に検出し、同時読み取り操作を許可しながら書き込み操作をサポートできます。このレベルでは、スナップショットを読み取ることができる限り、データベースのコピーを定期的に同期できます。この分離レベルは、書き込み操作が非常に少なく、読み取り操作と競合したくない場合、およびクエリ結果の一貫性が必要な場合に使用するのが最適です。
例:: 変更よりも頻繁にクエリが実行され、最新の値のみを保持する通貨換算テーブルまたはクエリ テーブル。
カーソル安定性分離は読み取りコミットを拡張し、多くのリレーショナル データのデフォルトの分離レベルです。この分離レベルでは、悲観的トランザクションは、別のステートメントで実行された場合に変更するレコードを指定する必要があります。これは通常、「SELECT」クエリに「FOR UPDATE」キーワードを追加することで実現されます。この場合、他の競合する読み取りおよび書き込みの悲観的なトランザクションは、トランザクションが終了するまでブロックされます。オプティミスティック トランザクションは、コミット時に検証される、変更されたすべてのレコード/エンティティのバージョン番号を追跡します。これは非常に一般的なオプティミスティック分離レベルであるため、すべての主要なオブジェクト リレーショナル マッピング ライブラリでサポートされています。 Java 永続性 API では、FLUSH_ON_COMMIT を使用してこのレベルに近づくことができ (ただし、クエリはローカルの変更に影響しない可能性があります)、競合が検出された場合は OptimisticLockException をスローします。この分離は、If-Match または If-Unmodified-Since HTTP ヘッダー フィールドでも使用でき、更新する前に以前のリソースのバージョンまたはタイムスタンプを比較するために使用できます。このレベルは、エンティティが外部情報 (データベースから読み込まれない) によって変更される場合、または変更が相互に上書きされない場合に最適に使用されます。
例: 共有会社ディレクトリまたは Wiki。
反復読み取りレベルによりカーソルの安定性が拡張され、トランザクション内のデータがトランザクション中に変更または削除されないことが保証されます。悲観的なトランザクションでは、すべてのレコードに対する読み取りロックが必要となり、他のサービスによるそれらのレコードの変更がブロックされます。オプティミスティック トランザクションは、すべてのレコードまたはエンティティを追跡し、コミット時にそれらが変更されたかどうかを確認します。このレベルは、エンティティの状態が他のエンティティに影響を与える可能性がある場合、またはトランザクションが読み取りおよび書き込み操作で構成される場合に最適に使用されます。
例: 1 つのエンティティから値を読み取り、それを使用して他のエンティティ値を計算する注文追跡データベース。
スナップショット分離により、スナップショット読み取りと反復読み取りが拡張され、トランザクション内で実行されるすべての読み取り操作がデータベース内の一貫したスナップショットを確認できるようになります。トランザクションによって実行される読み取り操作は、トランザクションの実行の初期または後期に関係なく、同じ結果になります。スナップショット分離によりファントム読み取り (クエリ結果が継続的に変化すること) が防止されるため、これは反復読み取りとは異なります。多くのリレーショナル データベースは、マルチバージョン同時実行制御 (SERIALIZABLE とも呼ばれる) を使用して、ロックと競合検出の組み合わせを通じてこのレベルをサポートします。このレベルでは、悲観的なメカニズムまたは楽観的なメカニズムと競合する可能性があることを考慮して、トランザクションをロールバックする準備をする必要があります。悲観的なメカニズムはリソースをロックすることで競合の可能性を減らそうとしますが、これらの変更はトランザクションがコミットされた後にマージする必要があります。オプティミスティック メカニズムでは、複数バージョンの同時実行制御も使用されますが、競合する可能性のある操作を引き起こす可能性のある他のトランザクションはブロックされず、代わりに競合するトランザクションがロールバックされます。このレベルの分離は、トランザクションが複数のレコードを読み取り、変更できる状況で使用するのが最適です。
例: システムステータスルールに基づくワークフローシステム。
シリアル化はスナップショット分離の拡張であり、すべてのトランザクションがシリアル化されているかのように次々に表示される必要があります。悲観的なメカニズムでは、書き込み操作が結果に影響を与えないように、評価されたすべてのクエリをロックする必要があります。オプティミスティック メカニズムは、評価されたすべてのクエリを追跡し、トランザクションの最後に後方検証モードまたは前方検証モードを使用して、並列書き込み操作が並列読み取り操作に影響を与えているかどうかを確認します。影響がある場合は、競合するトランザクションを除くすべてのトランザクションがロールバックされます。この分離レベルでは、コミットされたトランザクションはシステムの表現状態を変更しません。このレベルの分離は、完全なデータの一貫性が必要な状況で使用するのが最適です。
例: 範囲クエリを実行して新しい値を計算する会計システム。
以下は、この記事で説明されている分離レベルの概要表です。これは、アプリケーションに最適なレベルを見つけるのに役立ちます。
さまざまな分離レベルで発生する可能性のある競合タイプのトランザクション:
脏写 | 脏读 | 混合状态 | 不一致读 | 覆写 | 不可重复 | 幻读 | 不一致性 | |
未提交读 | 不可以 | 可以 | 可以 | 可以 | 可以 | 可以 | 可以 | 可以 |
已提交读 | 不可以 | 不可以 | 可以 | 可以 | 可以 | 可以 | 可以 | 可以 |
单调视图 | 不可以 | 不可以 | 不可以 | 可以 | 可以 | 可以 | 可以 | 可以 |
快照读取 | 不可以 | 不可以 | 不可以 | 不可以d | 可以 | 可以 | 可以 | 可以 |
游标稳定性 | 不可以 | 不可以 | 可以 | 可以 | 不可以 | 可以 | 可以 | 可以 |
可重复读取 | 不可以 | 不可以 | 可以 | 可以 | 不可以 | 不可以 | 可以 | 可以 |
快照隔离 | 不可以 | 不可以 | 不可以 | 不可以 | 不可以 | 不可以 | 不可以 | 可以 |
可串行性 | 不可以 | 不可以 | 不可以 | 不可以 | 不可以 | 不可以 | 不可以 | 不可以 |
さまざまな分離レベルの最適な前提条件:
缓冲 | 数据同步 | 乐观冲突模式 | 建议操作 | 例子 | |
未提交读 | 允许缓冲 | 间歇的 | 检测脏写 | 不能并发读写 | 档案 |
已提交读 | 允许缓冲 | 间歇的 | 没有冲突检测 | 单调的读/写 | Web论坛 |
单调视图 | 必须被验证 | 周期的 | 没有冲突检测 | 组合读入 | 用户偏好 |
快照读取 | 必须被验证 | 周期的 | 对比读入与修改内容 | 一致性读入 | 查询表 |
游标稳定性 | 允许缓冲 | 间歇的 | 对比修改的实体版本 | CRUD服务 | 目录 |
可重复读取 | 允许缓冲 | 间歇的 | 对比读入的实体版本 | 读/写实体 | 订单跟踪 |
快照隔离 | 必须被验证 | 周期的 | 对比读入的实体版本 | 同步实体 | 工作流 |
可串行性 | 必须被验证 | 完整同步 | 对比查询与修改内容 | 完善数据一致性 | 账目 |
データベース アプリケーションではデータの一貫性が非常に重要です。これにより、開発者は分散環境でデータを操作できるようになります。シリアル化可能性などの強力な整合性レベルは単純なプログラミング モデルを提供しますが、多くのアプリケーションにとって不必要な過剰なオーバーヘッド、操作のブロック、またはトランザクションのロールバックが発生する可能性があります。他に問題がある場合は、より適切な分離レベルを使用することで、開発者やシステム設計者がパフォーマンスとオーバーヘッドのバランスを維持しながらデータの一貫性の要件をよりよく理解できるようになります。
英語の原文を表示: Web 開発者が知っておくべき 8 つの分離レベル。