MongoDBではどのように楽観的ロックを実現していますか
MongoDB では楽観ロックは、ドキュメントにバージョン番号やタイムスタンプを追加することで実現されます。
- バージョン番号を使用する:ドキュメントにバージョン番号を格納するフィールドを追加し、ドキュメントが更新されるたびにバージョン番号を 1 加算します。複数のクライアントが同時に同じドキュメントを更新する場合はバージョン番号を比較し、バージョン番号が一致するクライアントのみがドキュメントを更新できます。
たとえば、users というコレクションがあり、ドキュメント構成が次のようになっています。
{
_id: ObjectId("5f7a43a822a0b03b504d918c"),
name: "John",
age: 30,
version: 1
}
このドキュメントを更新するには、次のコードを使用できます。
db.users.updateOne(
{ _id: ObjectId("5f7a43a822a0b03b504d918c"), version: 1 },
{ $set: { age: 31 }, $inc: { version: 1 } }
)
もし、他のクライアントがあなたより先にドキュメントを更新していた場合、その更新操作は正しいバージョン番号と一致しないことになるので、ドキュメントは更新に失敗するだろう。
- タイムスタンプを使用する:ドキュメントに最終更新日時タイムスタンプを格納するためのフィールドを追加します。ドキュメントを更新するときはいつでも、このタイムスタンプを現在時刻に更新します。複数のクライアントが同時に同じドキュメントを更新した場合は、タイムスタンプを比較し、最後に更新した日時が一致した場合のみ、ドキュメントを正常に更新できます。
例えば、usersというコレクションがあり、ドキュメント構造が以下のようになっています。
{
_id: ObjectId("5f7a43a822a0b03b504d918c"),
name: "John",
age: 30,
lastUpdated: ISODate("2021-01-01T00:00:00Z")
}
以下のコードを使用して、この文書を更新できます。
db.users.updateOne(
{ _id: ObjectId("5f7a43a822a0b03b504d918c"), lastUpdated: ISODate("2021-01-01T00:00:00Z") },
{ $set: { age: 31 }, $set: { lastUpdated: new Date() } }
)
他のクライアントがあなたより前にドキュメントに変更を加えている場合、彼らの変更は適切な最終更新時間に紐付きません。その結果、ドキュメントの更新は成功しません。
楽観的ロックはあくまでアプリケーションレベルで排他制御を行うもので、競合の発生を完全に防げるものではない点に注意が必要です。同時アクセスの多い場面では、複数クライアントが同時にバージョン番号やタイムスタンプのチェックを行い、ドキュメントの更新を試行する場合があり、競合の処理は引き続きアプリケーションレベルで行う必要があります。