Out of Memory

本ブログは更新を停止しました。Aerieをよろしくお願いいたします。

目次

Blog 利用状況

ニュース

2009年3月31日
更新を停止しました。引き続きAerieを御愛顧くださいませ。
2009年2月3日
原則としてコメント受付を停止しました。コメントはAerieまでお願いいたします。
詳細は2月3日のエントリをご覧ください。
2008年7月1日
Microsoft MVP for Developer Tools - Visual C++ を再受賞しました。
2008年2月某日
MVPアワードがVisual C++に変更になりました。
2007年10月23日
blogタイトルを変更しました。
2007年7月1日
Microsoft MVP for Windows - SDKを受賞しました!
2007年6月20日
スキル「ニュース欄ハック」を覚えた!
2006年12月14日
記念すべき初エントリ
2006年12月3日
わんくま同盟に加盟しました。

カレンダー

Network Error.

中の人

αετο? / aetos / あえとす

シャノン? 誰それ。

顔写真

埼玉を馬鹿にする奴は俺が許さん。

基本的に知ったかぶり。興味を持った技術に手を出して、ちょっと齧りはするものの、それを応用して何か形にするまでは及ばずに飽きて放り出す人。

書庫

日記カテゴリ

OracleではSelectを止められない?

排他制御とは、1つのリソースに複数のスレッドからアクセスするときに、調停を行うことだ。
教科書的な排他制御の説明はこんな感じだろう。

ある変数に対して、2つのスレッドがインクリメントを試みる。
インクリメントとは、

  1. 変数の現在の値を取得する
  2. それに1を足す
  3. 足した結果を変数に書き戻す

という一連の操作である。
変数の初期値が1であるとして、2つのスレッドが競合すると、

  1. スレッドAが変数から1を読み取る。
  2. スレッドBが変数から1を読み取る。
  3. スレッドAが読み取った値に1を足す
  4. スレッドBが読み取った値に1を足す。
  5. スレッドAが結果を書き戻す。変数の値は2になる。
  6. スレッドBが結果を書き戻す。変数の値は2になる。

となる。
インクリメントが2回行われているから、結果は3にならなければいけないのに、2になってしまう。
これを防ぐために、ロックが使われる。

  1. スレッドAが変数から1を読み取る。
  2. スレッドBが変数を読み取ろうとするが、スレッドAがロックしているため待つ。
  3. スレッドAが読み取った値に1を足す。
  4. スレッドAが結果を書き戻す。変数の値は2になる。
  5. スレッドAがロックを解放する。
  6. スレッドBが動き出し、変数から2を読み取る。
  7. スレッドBが読み取った値に1を足す。
  8. スレッドBが結果を書き戻す。変数の値は3になる。

万事めでたし、というわけだ。

データベースで言うと、変数から読み取る操作がSelect、書き戻す操作がInsertやUpdateになる。
上記の例で言えば、スレッドAがロックをかけたら、スレッドBのSelectも止められなければならない。でなければ、スレッドAが更新した後の値を読み取れないからだ。

だが、どうもOracleには、他人がSelectするのを止める術が無いように思える(Select for Updateなら止められるが、更新がInsertのケースでは使えない)。
SQL Serverでは可能らしいと聞くのだが。

こういう場合、Oracleでは、

  • 同じ行をUpdateするなら、Select for Updateか、タイムスタンプ列を使った楽観的排他制御
  • 新しい行をInsertするなら、該当列に一意性制約をかけておき、成功するまでループ

という方法になるのだろうか。
Insertの際に一意でなければならない列の値をユーザに決定させる場合は、成功するまで何度でもトライアンドエラーさせるしかなさそうだ。

投稿日時 : 2008年3月26日 10:46

Feedback

# re: OracleではSelectを止められない? 2008/03/26 11:03 はつね

Oracleの読み取り一貫性は、ロックをかけて変更しているときは、変更前の値がとれる仕組みですね。INSERTのときはINSERT前=そのレコードがない状態になりますね。

UPDATEのときはSELECT~FOR UPDATE、INSERTのときはINSERT実行時に他が同じPKに対してUPDATE/INSERTしていればそのSQL文またはトランザクションが完了するまでロック解除待ちになります。
※FOR UPDATEにはNO WAITオプションあり

# re: OracleではSelectを止められない? 2008/03/26 11:26 Streetw☆

>・同じ行をUpdateするなら、Select for Updateか、タイムスタンプ列を使った楽観的排他制御

私は楽観的ロックでしてます。
そのときの条件は、読み取ったときの値(+1する前の値)を渡してます。

>・新しい行をInsertするなら、該当列に一意性制約をかけておき、成功するまでループ

insertに失敗したらupdateに切り替えないといけないですね。

それとたいていは、自立型トランザクションにする必要があると思います~

>SQL Serverでは可能らしいと聞くのだが。

マルチバージョニングがなかったころのREAD COMMITTEDの動作の話ですね。
#確か今も互換性のためにデフォルトは同じだったかな。。

# re: OracleではSelectを止められない? 2008/03/26 11:54 シャノン

> INSERTのときはINSERT前=そのレコードがない状態になりますね。
> INSERTのときはINSERT実行時に他が同じPKに対してUPDATE/INSERTしていればそのSQL文またはトランザクションが完了するまでロック解除待ちになります。

ですね。
で、同じ値をInsertしようとすると、ロックが解除された時点で制約違反で例外が飛ぶと。
つまり、「ある値を持ったデータがあるかどうかチェックして、なければInsert」という処理は、ロックを使ったとしても不可能である、ということになりますね。

> 私は楽観的ロックでしてます。
> そのときの条件は、読み取ったときの値(+1する前の値)を渡してます。

それでもいいですね。

> insertに失敗したらupdateに切り替えないといけないですね。

いや、切り替えちゃ駄目なケースを想定してます。
キーがある値であるレコードが存在しなければその値でinsert、あれば別の値に変えてinsertする場合です。

> 自立型トランザクションにする必要があると思います~

また知らん言葉が出てきた。

> マルチバージョニングがなかったころのREAD COMMITTEDの動作の話ですね。

今は SQL Server でも Select は止めないのが普通なんですかね。

タイトル
名前
Url
コメント