Repeatable Read 分離で SELECT FOR UPDATE を使用する

このページでは、反復可能読み取り分離FOR UPDATE 句を使用する方法について説明します。

FOR UPDATE 句のロック メカニズムは、繰り返し可能な読み取りとシリアル化可能な分離で異なります。直列化可能分離とは異なり、Repeatable Read 分離では FOR UPDATE 句はロックを取得しません。FOR UPDATE のロックの詳細については、シリアル化可能な分離で SELECT FOR UPDATE を使用するをご覧ください。

FOR UPDATE 句の使用方法については、GoogleSQLPostgreSQLFOR UPDATE リファレンス ガイドをご覧ください。

FOR UPDATE 句を使用する理由

トランザクションが反復可能読み取り分離で実行される場合、SELECT ステートメントによってクエリされたデータは、トランザクションの確立されたスナップショット タイムスタンプで常に返されます。トランザクションがクエリされたデータに基づいて更新を行う場合、同時実行トランザクションもクエリされたデータを更新すると、正確性の問題が発生する可能性があります。詳細については、読み取り / 書き込みの競合と正確性をご覧ください。

トランザクションがコミットされたときに SELECT ステートメントでクエリされたデータが有効であることを確認するには、繰り返し読み取り分離で FOR UPDATE 句を使用します。FOR UPDATE を使用すると、読み取りと変更の間に別のトランザクションによってデータが変更された可能性がある読み取り / 書き込みの競合が発生した場合でも、トランザクションの正確性が保証されます。

クエリ構文

このセクションでは、FOR UPDATE 句を使用する場合のクエリ構文について説明します。

最も一般的な使用方法は、最上位の SELECT ステートメントです。次に例を示します。

SELECT SingerId, SingerInfo
FROM Singers WHERE SingerID = 5
FOR UPDATE;

FOR UPDATE 句は、トランザクションが commit されるときに SELECT ステートメントと SingerID = 5 によってクエリされたデータが有効であることを保証します。これにより、同時実行トランザクションがクエリされたデータを更新した場合に発生する可能性のある正確性の問題を防ぎます。

WITH ステートメントで使用する

WITH ステートメントの外側レベルのクエリで FOR UPDATE を指定するときに、FOR UPDATE 句は WITH ステートメント内でスキャンされた範囲を検証しません。

次のクエリでは、FOR UPDATE が共通テーブル式(CTE)クエリに伝播されないため、スキャンされた範囲は検証されません。

WITH s AS (SELECT SingerId, SingerInfo FROM Singers WHERE SingerID > 5)
SELECT * FROM s
FOR UPDATE;

CTE クエリで FOR UPDATE 句が指定されている場合、CTE クエリのスキャン範囲が検証されます。

次の例では、SingerId > 5 が検証されている行の SingerId セルと SingerInfo セルが検証されています。

WITH s AS
  (SELECT SingerId, SingerInfo FROM Singers WHERE SingerId > 5 FOR UPDATE)
SELECT * FROM s;

サブクエリで使用する

FOR UPDATE 句は、1 つ以上のサブクエリを含む外側レベルのクエリで使用できます。最上位クエリとサブクエリ内でスキャンされた範囲は、式サブクエリを除き、検証されます。

次のクエリは、SingerId > 5. の行の SingerId セルと SingerInfo セルを検証します。

(SELECT SingerId, SingerInfo FROM Singers WHERE SingerId > 5) AS t
FOR UPDATE;

次のクエリは式サブクエリ内にあるため、Albums テーブルのセルは検証されません。式サブクエリから返された行の SingerId セルと SingerInfo セルが検証されます。

SELECT SingerId, SingerInfo
FROM Singers
WHERE SingerId = (SELECT SingerId FROM Albums WHERE MarketingBudget > 100000)
FOR UPDATE;

ビューのクエリに使用する

次の例に示すように、FOR UPDATE 句を使用してビューをクエリできます。

CREATE VIEW SingerBio AS SELECT SingerId, FullName, SingerInfo FROM Singers;

SELECT * FROM SingerBio WHERE SingerId = 5 FOR UPDATE;

ビューを定義するときに FOR UPDATE 句は使用できません。

サポートされていないユースケース

次の FOR UPDATE のユースケースはサポートされていません。

  • Spanner の外部でコードを実行するための相互排除のメカニズムとして: Spanner のロックを使用して、Spanner の外部のリソースへの排他的アクセスを確保しないでください。トランザクションが Spanner によって中断される場合があります。たとえば、トランザクションが再試行された場合、アプリケーション コードによって明示的に試行されたか、Spanner JDBC ドライバなどのクライアント コードによって暗黙に試行されたかにかかわらず、トランザクションの試行が実際に行われている間にロックが実施されたことのみが保証されます。
  • LOCK_SCANNED_RANGES ヒントと組み合わせる: 同じクエリで FOR UPDATE 句と LOCK_SCANNED_RANGES ヒントの両方を使用することはできません。使用すると、Spanner からエラーが返されます。詳細については、LOCK_SCANNED_RANGES ヒントとの比較をご覧ください。
  • 全文検索クエリ: 全文検索インデックスを使用するクエリで FOR UPDATE 句を使用できません。
  • 読み取り専用トランザクション: FOR UPDATE 句は、読み取り / 書き込みトランザクション内で実行されるクエリでのみ有効です。
  • DDL ステートメント内: DDL ステートメント内のクエリで FOR UPDATE 句を使用できません。これらのクエリは、後で実行するために保存されます。たとえば、ビューを定義するときに FOR UPDATE 句を使用できません。

次のステップ