質問: 悲観的ロックの例でよく登場する `SELECT FOR UPDATE` があります。 一方で、トランザクション分離レベルというものがあります。 トランザクション分離レベル - Wikipedia からトランザクション分離レベルについての説明の一部を引用すると > > ANSI/ISO SQL標準で定められている分離レベルは、下記の4種類で定義されている。 > > > SERIALIZABLE(直列化可能) > > REPEATABLE READ(読み取り対象のデータを常に読み取る) > > READ COMMITTED(確定した最新データを常に読み取る) > > READ UNCOMMITTED(確定していないデータまで読み取る) > > > とあります。 `SELECT FOR UPDATE`は上記4つでいうと、どれなんでしょうか?(SERIALIZABLEが該当?)それともまったく違う概念なのでしょうか?
回答: どのようにトランザクション分離レベルを実装するかが、RDB ごとに少し違っていた記憶があるので、 MySQL に限定して回答を行います。 MySQL においては、トランザクション分離レベルと `SELECT FOR UPDATE` は、共に、トランザクションを並列でさばくための機構です。 MySQL において、トランザクション間の整合性は、次のような原則で動作します。 1. トランザクションが一環した読み込みを行う必要がある場合(e.g. REPEATABLE READ)、整合性を担保するべき時刻(e.g. トランザクション開始時刻)と undo log を利用して、その時刻相当のデータを常に読み込む。 2. トランザクションが書き込みを行う場合、データベースの最新の値を更新する。競合する更新がある場合、それはロックによって排他制御を行い、トランザクション間の整合性が崩れないようにする。 これを実現するために、 MySQL(+innodb)は、 shared lock (読み取りロック)と exclusive lock (書き込みロック)を内部機構として持っています。 shared lock と exclusive lock は同一対象に対して、同時には取得できません。 shared lock は、 shared lock であれば、そのロックを共有できます。 (セマフォのような動作になります) exclusive lock は mutex のような挙動です。 これら lock を用いるとどうしてトランザクション分離レベルが実装できるかというと、一番条件が厳しい serializable がどうなっているか、を例示すれば、雰囲気がなんとなく分かるかな、と思っています。 MySQL において、トランザクションに serializable を指定する、ということは、 1. その読み込み(select) に対しては shared lock の取得が勝手に行われます。 2. 書き込みに対しては exclusive lock が取得されます。 これによって、直列化不能になるような競合するトランザクションたちが実行された場合、 MySQL ではどちらかが wait します。(おたがいがおたがいを wait すると、 deadlock になります。) 以上が、 MySQL においてトランザクション分離がどのように実現されているかを説明しました。内部的には、 shared lock と exclusive lock という機構でこれが実現されています。 `SELECT FOR UPDATE` は、 select しながら exclusive lock を敢えて取得しろ、ということを意味する構文です。なので、トランザクション分離レベルも、 `SELECT FOR UPDATE`も、少くとも MySQL においては、内部の lock の挙動をどのように行うか、という意味での並列実行制御のレベルの話で、同じレイヤーの話である、と個人的には考えています。