私は午前中すべてのトップ記事を読んで過ごしました Googleは楽観的ロックを解き放ちます 、そして私の人生において、私はまだ本当にそれを理解していません。
I nderstand楽観的ロックには、レコードの "バージョン"を追跡するための列の追加が含まれ、この列はタイムスタンプ、カウンター、またはその他のバージョン追跡構成にすることができます。しかし、それでもWRITE整合性がどのように保証されるかはまだわかりません(つまり、複数のプロセスが同じエンティティを同時に更新している場合、その後、エンティティは本来あるべき状態を正しく反映します)。
誰かがJava(againstに対して)楽観的ロックをどのように使用できるかの例を具体的でわかりやすいの例で提供できますか? 、たぶん、MySQL DB)Person
エンティティがあるとします。
public class Person {
private String firstName;
private String lastName;
private int age;
private Color favoriteColor;
}
そして、そのPerson
インスタンスがpeople
MySQLテーブルに永続化されます。
CREATE TABLE people (
person_id PRIMARY KEY AUTO_INCREMENT,
first_name VARCHAR(100) NOT NULL,
last_name VARCHAR(100) NOT NULL, # } I realize these column defs are not valid but this is just pseudo-code
age INT NOT NULL,
color_id FOREIGN KEY (colors) NOT NULL # Say we also have a colors table and people has a 1:1 relationship with it
);
次に、同じPerson
エンティティを同時に更新しようとしている2つのソフトウェアシステム、または2つのスレッドを備えた1つのシステムがあるとします。
people
テーブルやcolors
テーブルに楽観的ロックを実装するにはどうすればよいですか? (特定のDDL例を探す)people
/colors
テーブルを正しく "最適にロック"するシナリオを誰かが実行できますか?基本的に、楽観的ロックの動作を見て、なぜ機能するのかをわかりやすく説明しています。通常、楽観的ロックを調べるときは、 Hibernate などのライブラリ、または _@Version
_ をサポートする他のJPA実装も使用します。
例は次のように読むことができます:
_public class Person {
private String firstName;
private String lastName;
private int age;
private Color favoriteColor;
@Version
private Long version;
}
_
これをサポートするフレームワークを使用していない場合は、明らかに_@Version
_アノテーションを追加する意味はありません。
DDLは
_CREATE TABLE people (
person_id PRIMARY KEY AUTO_INCREMENT,
first_name VARCHAR(100) NOT NULL,
last_name VARCHAR(100) NOT NULL, # } I realize these column defs are not valid but this is just pseudo-code
age INT NOT NULL,
color_id FOREIGN KEY (colors) NOT NULL, # Say we also have a colors table and people has a 1:1 relationship with it
version BIGINT NOT NULL
);
_
他のプロセスが両方のステップ間でデータを変更するリスクを冒すことなく両方のステップを実行するには、通常、次のようなステートメントで処理されます。
_UPDATE Person SET lastName = 'married', version=2 WHERE person_id = 42 AND version = 1;
_
ステートメントを実行した後、行を更新したかどうかを確認します。変更した場合、データを読んだので誰もデータを変更しませんでした。それ以外の場合は、他の誰かがデータを変更しました。他の誰かがデータを変更した場合、通常、使用しているライブラリによって OptimisticLockException
を受け取ります。
この例外により、すべての変更が取り消され、値を変更するプロセスが再開されます。これは、エンティティーが更新される条件が適用されなくなる可能性があるためです。
だから衝突はない:
衝突:
Colorが別のオブジェクトの場合は、同じスキームでバージョンを配置する必要があります。
OptimisticLockException
sを取得することが期待されます多くの異なるアプリケーションがデータにアクセスする場合は、データベースによって自動的に更新される列を使用するのが最善の方法です。例えばMySQLの場合
_version TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;
_
このように、楽観的ロックを実装するアプリケーションは、ダムアプリケーションによる変更に気づきます。
TIMESTAMP
の解像度またはそれのJava解釈よりも頻繁にエンティティを更新すると、このアプローチは特定の変更の検出に失敗する可能性があります。また、Java新しいTIMESTAMP
を生成させる場合は、アプリケーションを実行しているすべてのマシンが完全に時間同期していることを確認する必要があります。
すべてのアプリケーションを整数、ロング、...に変更できる場合、通常はバージョンが適切なソリューションです。これは、クロックの設定が異なることによる影響を受けないためです;-)
他のシナリオがあります。たとえば、行を変更するたびに、ハッシュを使用するか、String
をランダムに生成します。重要なのは、プロセスがバージョン列を確認しても変更を検出できないため、プロセスがローカル処理またはキャッシュ内にデータを保持している間は値を繰り返さないことです。
最後の手段として、すべてのフィールドの値をバージョンとして使用できます。これはほとんどの場合で最も費用のかかるアプローチですが、テーブル構造を変更せずに同様の結果を得る方法です。 Hibernateを使用する場合、この動作を強制する _@OptimisticLocking
_ -annotationがあります。エンティティの読み取り後に行が変更された場合にエンティティクラスで@OptimisticLocking(type = OptimisticLockType.ALL)
を使用して失敗するか、別のプロセスが変更したフィールドを変更したときに@OptimisticLocking(type = OptimisticLockType.DIRTY)
だけで失敗します。
@TheConstructor:それが何であるか、そうでないかについての素晴らしい説明。 「オプティミスティックロックは競合する変更をマージする魔法ではない」と言ったとき、私はコメントしたいと思いました。以前は、ユーザーがフォーム上のレコードを編集できるDataFlexアプリケーションを管理していました。ユーザーが「保存」ボタンを押すと、アプリはデータの「マルチユーザー再読み取り」と呼ばれる処理を実行し、現在の値を取得して、ユーザーが変更した内容と比較します。ユーザーが変更したフィールドがその間に変更されていなかった場合、それらのフィールドだけがレコードに書き戻され(再読み取り+書き込み操作中にのみロックされます)、したがって、2人のユーザーが異なるフィールドを透過的に変更できます問題のない同じレコード。バージョンスタンプは不要で、どのフィールドが変更されたかを知るだけで済みました。
確かに、これは完璧な解決策ではありませんが、その場合はうまくいきました。それは楽観的であり、無関係な変更を許可し、競合する変更についてユーザーにエラーを与えました。これは、SQLを実行する前に実行できる最高の方法でしたが、おそらくオブジェクトやWeb関連のシナリオでは、今日でも優れた設計原則です。