2009年8月1日土曜日

SQL Serverのトランザクション分離レベル

ADO.NETで自動トランザクションを使って開発するとデフォルトで、トランザクション分離レベルが'Serializable'となります。
なので、
以下の左のような、タスクごとに一番最後に報告した勤務時間のユーザIDを取得するというSQLを実行した場合、トランザクションを閉じるまで、右のような過去の実績報告を追加するということができません(追加だけでなく、勤務時間表のすべての更新作業(追加、更新、削除)ができないはずです)。



これは、Serializableの動きのためです。
この時点のロック状況は、こんな感じ。


Serializableの特徴であるファントム回避のためキーをレンジでロックしています。
報告日時で適切なインデックスがないためオールレンジロックと同様な結果となります。
なので、insert文のロックは、Waitになっています。


このSQLでは場合は、レンジロックが有効に働くインデックスは掛けるのは不可能だと思います。
なので、Select文を速く終わらせるように工夫するか、nolockを付ける(=ダーティリードを許す)かトランザクション分離レベルをSerializableでなくする(=ファントムを許す)ことをしなければなりません。



これが、Serializableの動きとしては正しいのですが、Oracle開発から来た人にはなかなか分かってもらえない。


ちなみに、分離レベルがRepeatable readであれば右のトランザクション中に左の更新ができました。





オラクル開発から来た人曰く、トランザクション分離レベルをリードコミットスナップショットを使うとよいというが、この変更は、テーブル単位ではなくDB単位に指定するものだし、ADO.NETのデフォルトはSerializableなので、プログラムをいじらなければ何も変わらない気がするし、ADO.NETでREADCOMMITTEDを使っているとDBを変えた瞬間から動きが変わる(ロックしなくなる)のでそれでよいかを検証しなければならないと思う。あんまり都合よくいかないような気がするんだけどなぁ。

0 件のコメント: