質問: 私のサーバーシステムでは READ UNCOMMITTED をむしろ使用したいような状況が発生致しました。 私の考えは正しいでしょうか? 掲示板アプリのサーバーにて、掲示板に投稿したとして以下の処理が発生します。 * トランザクション + トークテーブルに投稿を格納(insert) + 掲示板テーブルの トータル投稿数更新(サブクエリではなくcount:=count+1で) と 最終投稿時間を更新(update) + 掲示板テーブルから トータル投稿数 をセレクト(select) * このトランザクション実行後、3番めのSQLで取得した投稿数が1000未満ならコミット そうではないならロールバック(投稿失敗) 要するに、投稿格納後に投稿数上限の1000を超えていないかのチェックを行います。 一見、 SERIALIZABLE がいいように思えますが(もちろんそうなのですが) 処理性能のことも考えると、むしろここは一番最弱な READ UNCOMMITTED を使用したほうがいい気がしてきました。 READ UNCOMMITTED なら 2つの投稿トランザクションが同時に処理された場合、どちらかが先に2つめのSQLを実行した時点で、もう片方はトランザクション中に3番めのSQLにてそのコミットされていない値を取得して、 最大投稿数以上投稿してしまうことを防げると思ったからです。 この場合、上記の場合は * 2つとも投稿処理が失敗する可能性(お互い、更新後にセレクトしたら) → そのぐらいべつにおk * 最新投稿時間がもう片方の投稿の時間になる可能性(セレクト順だけ逆転する) → とは言えど、同時に発生した処理のためその差は数ミリ秒程度なのでおk * 片方だけロールバック発生したらカウントがおかしくなることがある(現在インクリメント系 count=count+1なので) → 重要な不具合なのですがそもそもこのカラムをなくし、毎回取得時にカウントすることにする が発生するかと思われますが、それは妥協とします。 このようにあえて READ UNCOMMITTED の弱点を利用するってのはやめるべきでしょうか?
回答: > > READ UNCOMMITTED なら > > 2つの投稿トランザクションが同時に処理された場合、どちらかが先に2つめのSQLを実行した時点で、もう片方はトランザクション中に3番めのSQLにてそのコミットされていない値を取得して、 > > 最大投稿数以上投稿してしまうことを防げると思ったからです。 > > > この前提は誤りです。 READ UNCOMMITTEDは「他のトランザクションによって変更されCOMMITされていないデータを**読む可能性がある**」のであって、「COMMITされていないデータを**必ず読む**」ではないのです。 999件のデータが登録された状態で2つのトランザクションが同時に走ったときに、 > > 掲示板テーブルの トータル投稿数更新(サブクエリではなくcount:=count+1で) と 最終投稿時間を更新(update) > > > ・想定通り999+1、1000+1となる ・両方のトランザクションが999+1を行う のどちらになるかはわかりません。