場合によっては、一貫した読み取りは便利ではありません。例えば、テーブル
child
に新しい行を追加したければ、子供がテーブル
parent
に親を持っている事を確認するでしょう。次の例は、応用コード内でどのように参照整合性を実装するのかを表しています。
テーブル parent
を読み取る為に一貫した読み取りを利用し、実際にテーブル内に子供の親を確認したと仮定してください。テーブル
child
に子供行を安全に追加する事ができますか?別のユーザが知らない間にテーブル
parent
から親行を削除する可能性があるので、答えはノーです。
この解決法は、LOCK IN SHARE MODE
を利用して SELECT
をロック
モードで実行する事です:
SELECT * FROM parent WHERE NAME = 'Jones' LOCK IN SHARE MODE;
共有モードで読み取りを行うというのは、まず最新の有効なデータを読み取り、そして読んだ行に共有モードを設定するという意味です。共有モード
ロックは、読み取った行が別の人によって更新されたり、削除されたりする事を防ぎます。また、もし最新データが別のクライアント接続のコミットされていないトランザクションに属していたら、そのトランザクションがコミットされるまで待ちます。先行クエリが親
'Jones'
を返すのを確認した後、child
テーブルに子レコードを安全に追加し、トランザクションをコミットする事ができます。
別の例を見てみましょう:テーブル
child
に追加された各子供に固有識別子を割り当てる為に利用する整数カウンタ
フィールドが、テーブル child_codes
内にあります。カウンタの現在値を読む為に、一貫した読み取りや、共有モード読み取りを利用する事は、そのデータベースの2ユーザが同じカウンタ値を確認する可能性があり、またその2ユーザが同じ識別子を利用してテーブルに子供を追加しようとすると複製キーエラーが発生する為、良い考えとは言えません。
もし2ユーザがカウンタを同時に読むと、少なくても1ユーザはカウンタを更新しようとする時にデッドロックになってしまう為、
LOCK IN SHARE MODE
はこの場合良い解決法とはいえません。
この場合、カウンタの読み取りとインクリメントを実装する為の良い方法が2つあります:(1)
カウンタを1でインクリメントする事で更新し、その後にだけ読み取る、または、
(2) ロックモード FOR UPDATE
を利用してまずカウンタを読み取り、その後にインクリメントする。後者の方法は、次のように実装できます:
SELECT counter_field FROM child_codes FOR UPDATE; UPDATE child_codes SET counter_field = counter_field + 1;
A SELECT ... FOR UPDATE
は、読み取る各行上に専用ロックを設定し、最新の有効データを読み取ります。従って、それは
SQL UPDATE
が行上に設定する物と同じロックを設定します。
前出の例は、ただ単に SELECT ... FOR
UPDATE
がどのように機能するかを表す例です。MySQL
内では、固有識別子を生成する特定のタスクは、実際にはテーブルへの単一アクセスの利用だけで達成する事ができます:
UPDATE child_codes SET counter_field = LAST_INSERT_ID(counter_field + 1); SELECT LAST_INSERT_ID();
SELECT
ステートメントはただ単に識別子情報を検索するだけです。(現在の接続特有の物)これはテーブルにアクセスしません。
IN SHARE MODE
と FOR
UPDATE
読み取りによって設定されたロックは、トランザクションがコミットされたりロールバックされたりした時にリリースされます。