第9章 ロックおよび同時実行
Red Hat Data Grid では、マルチバージョンコンカレンシー制御(MVCC)が使用されます。これは、リレーショナルデータベースやその他のデータストアで人気のある同時実行スキームです。MVCC には、粗粒度の Java 同期や、共有データにアクセスするための JDK ロックに比べて、次のような多くの利点があります。
- 同時リーダーとライターの許可
- リーダーとライターが互いにブロックしない
- 書き込みスキューを検出して処理できる
- 内部ロックのストライピングが可能
9.1. 実装の詳細のロック リンクのコピーリンクがクリップボードにコピーされました!
Red Hat Data Grid の MVCC 実装では、最小限のロックと同期が使用され、可能な限り 比較やスワップ やロックのないデータ構造などのロックのない手法を多用します。これにより、マルチ CPU やマルチコア環境の最適化に役立ちます。
特に、Red Hat Data Grid の MVCC 実装はリーダー向けに大幅に最適化されています。リーダースレッドは、エントリーの明示的なロックを取得せず、代わりに問題のエントリーを直接読み込みます。
一方、ライターは、書き込みロックを取得する必要があります。これにより、エントリーごとに 1 つの同時書き込みのみが保証されるため、同時ライターはキューイングしてエントリーを変更することになります。同時読み取りを可能にするため、ライターはエントリーを MVCCEntry でラップして、変更する予定のエントリのコピーを作成します。このコピーは、同時リーダーが部分的に変更された状態を認識できないようにします。書き込みが完了したら、MVCCEntry.commit() はデータコンテナーへの変更をフラッシュし、後続のリーダーに変更内容が反映されます。
9.1.1. クラスター化されたキャッシュでの仕組み リンクのコピーリンクがクリップボードにコピーされました!
クラスター化されたキャッシュでは、各キーにキーをロックするノードがあります。このノードはプライマリー所有者と呼ばれます。
9.1.1.1. 非トランザクションキャッシュ リンクのコピーリンクがクリップボードにコピーされました!
- 書き込み操作はキーのプライマリー所有者に送信されます。
プライマリー所有者はキーをロックしようとします。
- 成功すると、操作が他の所有者に転送されます。
- そうでない場合は、例外が発生します。
操作が条件付きで、プライマリオーナーで失敗した場合、他のオーナーには転送されません。
操作がプライマリ所有者でローカルに実行される場合、最初のステップはスキップされます。
9.1.2. トランザクションキャッシュ リンクのコピーリンクがクリップボードにコピーされました!
トランザクションキャッシュは、楽観的および悲観的ロックモードをサポートします。詳細は、「 トランザクションロック 」のセクションを参照してください。
9.1.3. 分離レベル リンクのコピーリンクがクリップボードにコピーされました!
分離レベルは、他のトランザクションと同時に実行されているときに読み取ることができるトランザクションに影響します。詳細は、「 分離レベル 」のセクションを参照してください。
9.1.4. LockManager リンクのコピーリンクがクリップボードにコピーされました!
LockManager は、書き込み用にエントリーをロックするコンポーネントです。LockManager は、LockContainer を使用して、ロックを検索、保持、作成します。LockContainers には、ロックストライピングをサポートするものと、エントリーごとに 1 つのロックをサポートするものの 2 つの大まかな特徴があります。
9.1.5. ロックストライピング リンクのコピーリンクがクリップボードにコピーされました!
ロックストライピングでは、固定サイズの共有ロックコレクションをキャッシュ全体に使用する必要があり、ロックはエントリーのキーのハッシュコードに基づいてエントリーに割り当てられます。JDK の ConcurrentHashMap がロックを割り当てる方法と同様に、これにより、関連性のない可能性のあるエントリが同じロックによってブロックされる代わりに、拡張性の高い固定オーバーヘッドのロックメカニズムが可能になります。
別の方法は、ロックストライピングを無効にすることです。これは、エントリーごとに 新しい ロックが作成されることを意味します。このアプローチでは、スループットが高くなる 可能性 がありますが、追加のメモリー使用量やガベージコレクションのチャーンなどのコストがかかります。
異なるキーのロックが同じロックストライプになってしまうとデッドロックが発生する可能性があるため、ロックストライピングはデフォルトで無効になっています。
ロックストライピングによって使用される共有ロックコレクションのサイズは、'<locking /> 設定要素の concurrencyLevel 属性を使用して調整できます。
設定例:
<locking striping="false|true"/>
<locking striping="false|true"/>
または
new ConfigurationBuilder().locking().useLockStriping(false|true);
new ConfigurationBuilder().locking().useLockStriping(false|true);
9.1.6. 同時実行レベル リンクのコピーリンクがクリップボードにコピーされました!
この同時実行レベルは、ストライプロックコンテナーのサイズを決定する他に、DataContainer の内部など、関連する JDK ConcurrentHashMap ベースのコレクションを調整するためにも使用されます。コンカレンシーレベルの詳細な説明は JDK ConcurrentHashMap Javadocs を参照してください。このパラメーターは Red Hat Data Grid でまったく同じ方法で使用されるためです。
設定例:
<locking concurrency-level="32"/>
<locking concurrency-level="32"/>
または
new ConfigurationBuilder().locking().concurrencyLevel(32);
new ConfigurationBuilder().locking().concurrencyLevel(32);
9.1.7. ロックタイムアウト リンクのコピーリンクがクリップボードにコピーされました!
ロックタイムアウトは、競合するロックを待つ時間 (ミリ秒単位) を指定します。
設定例:
<locking acquire-timeout="10000"/>
<locking acquire-timeout="10000"/>
または
new ConfigurationBuilder().locking().lockAcquisitionTimeout(10000); //alternatively new ConfigurationBuilder().locking().lockAcquisitionTimeout(10, TimeUnit.SECONDS);
new ConfigurationBuilder().locking().lockAcquisitionTimeout(10000);
//alternatively
new ConfigurationBuilder().locking().lockAcquisitionTimeout(10, TimeUnit.SECONDS);
9.1.8. 一貫性 リンクのコピーリンクがクリップボードにコピーされました!
(すべての所有者がロックされているのとは対照的に) 単一の所有者がロックされるという事実により、次の一貫性の保証が失われることはありません。キー K がノード {A, B} に対してハッシュ化され、トランザクション TX1 が、たとえば、A 上の K のロックを取得したとします。別のトランザクション TX2 が B (またはその他のノード) 上で開始され、TX2 が K のロックを試みる場合、ロックがすでに TX1 によって保持されているため、タイムアウトでロックに失敗します。理由は、キー K のロックがトランザクションの発生場所に関係なく、常に、確定的に、クラスターの同じノードで取得されるからです。