- PR -

Insertが先 Updateが先 

投稿者投稿内容
rucio
ベテラン
会議室デビュー日: 2002/11/27
投稿数: 98
投稿日時: 2008-07-24 08:57
SQL Serverという前提で、私もWITH(UPDLOCK)がいいと思います。
SELECT ... FROM ... WITH (UPDLOCK) WHERE ...
という感じです。
3番ということになりますね。

カーニー
ぬし
会議室デビュー日: 2003/09/04
投稿数: 358
お住まい・勤務地: 東京
投稿日時: 2008-07-24 10:17
引用:
あしゅさんの書き込み (2008-07-23 23:13) より:
引用:
のん吉さんの書き込み (2008-07-23 17:59) より:
@Insertで重複エラーをキャッチしたらUpdateをする


この手法はPostgreSQLだと問題が起きます。
エラーが発生したトランザクションはロールバックしか出来ないので。
それ以外のDBの場合は、この選択肢の中で確実に更新出来るのはこれだけです。


PostgreSQL 8.0でトランザクションのSAVEPOINTがサポートされました。
INSERT前にSAVEPOINTを実行しておけば、INSERT失敗後にROLLBACK TOで戻って、UPDATEを再試行することが可能です。

#つーか、PostgreSQLのこの仕様だけは許せない。
未記入
大ベテラン
会議室デビュー日: 2008/02/07
投稿数: 115
投稿日時: 2008-07-24 11:16
盛り上がってますね。

このケースで、select for update や UPDLOCK はどのような効果があるのでしょうか?対象行が存在すれば行をロックすることができますが、対象行が存在しなければロックをおこなうことができませんよね。結果として「UPDATE できないから INSERT を試みる」というプロセスが同時に複数発生することを抑制できないように思います。

このケースでは、select for update や UPDLOCK は意味を持たず、直接 UPDATE ステートメントを実行するのと同じではありませんか?この方法で、他プロセスとの排他制御を適切におこなえるとは思えません。

データベースだけで確実に排他制御をおこなうには、テーブルロックするしかないように思えます。他には、「これらの INSERT/UPDATE 操作をおこなうには、事前に○○を排他的に取得していなければならない」というルールを作りアプリケーションレベルで排他制御をおこなうことも考えられますが、ルールを守らないアプリケーションが直接 UPDATE/INSERT 操作をおこなう可能性を排除できません。私は、パフォーマンスを考慮して、テーブルロックではなくアプリケーションレベルで排他制御をおこなうことが多いです。
あしゅ
ぬし
会議室デビュー日: 2005/08/05
投稿数: 613
投稿日時: 2008-07-24 12:42
引用:

カーニーさんの書き込み (2008-07-24 10:17) より:
PostgreSQL 8.0でトランザクションのSAVEPOINTがサポートされました。
INSERT前にSAVEPOINTを実行しておけば、INSERT失敗後にROLLBACK TOで戻って、UPDATEを再試行することが可能です。


おお。その手がありますね。気づきませんでした。

私は基本的にJava屋なので.NETでどうなのか知りませんが、
トランザクションコーディネータを利用した場合はどうなのでしょうか?

引用:

未記入さんの書き込み (2008-07-24 11:16) より:
このケースで、select for update や UPDLOCK はどのような効果があるのでしょうか?対象行が存在すれば行をロックすることができますが、対象行が存在しなければロックをおこなうことができませんよね。結果として「UPDATE できないから INSERT を試みる」というプロセスが同時に複数発生することを抑制できないように思います。


その通りだと思います。

引用:

データベースだけで確実に排他制御をおこなうには、テーブルロックするしかないように思えます。他には、「これらの INSERT/UPDATE 操作をおこなうには、事前に○○を排他的に取得していなければならない」というルールを作りアプリケーションレベルで排他制御をおこなうことも考えられますが、ルールを守らないアプリケーションが直接 UPDATE/INSERT 操作をおこなう可能性を排除できません。私は、パフォーマンスを考慮して、テーブルロックではなくアプリケーションレベルで排他制御をおこなうことが多いです。


そうですね。確実に行う方法はテーブルロックだけでしょう。
ただ、アプリケーションでの排他制御は負荷分散した場合に
問題が起きるので避けた方がいいと思います。
渋木宏明(ひどり)
ぬし
会議室デビュー日: 2004/01/14
投稿数: 1155
お住まい・勤務地: 東京
投稿日時: 2008-07-24 14:23
引用:

引用:

3 の場合、Select の結果「既存レコードがない」と分かったとして、Insert するまでの間に別のセッションから Insert されたらどーしましょ?


この場合は例外として処理します。



そっか、登録順の制御しないならそれでもいいですね。
逆に登録順の制御が必要なら、もっと別のところで制御しなくちゃならないし。
カーニー
ぬし
会議室デビュー日: 2003/09/04
投稿数: 358
お住まい・勤務地: 東京
投稿日時: 2008-07-24 14:52
引用:
あしゅさんの書き込み (2008-07-24 12:42) より:
引用:

データベースだけで確実に排他制御をおこなうには、テーブルロックするしかないように思えます。


そうですね。確実に行う方法はテーブルロックだけでしょう。



つまりファントムが問題なわけで、トランザクション分離レベルをSerializableにすれば解決できるかな?
もっとも、ほとんどのRDBMSでは同時実行性が大幅に低下すると思いますが。
あしゅ
ぬし
会議室デビュー日: 2005/08/05
投稿数: 613
投稿日時: 2008-07-24 18:20
引用:

カーニーさんの書き込み (2008-07-24 14:52) より:
つまりファントムが問題なわけで、トランザクション分離レベルをSerializableにすれば解決できるかな?
もっとも、ほとんどのRDBMSでは同時実行性が大幅に低下すると思いますが。


ファントムではないと思います。
見えていたはずのデータが見えないわけではないですし。

OracleやPostgreSQLのようなスナップショット型の分離レベルを
採用しているDBMSではSERIALIZABLEではこの問題は解決できません。
このような状況だけに限りませんが、再試行が必要になります。

#というか、前に見た時より新くなっていることを検出して
#トランザクションの結果をロールバックのみに設定するだけ。

SERIALIZABLEが伝統的なインデックスなどの読み取りロックで実現されて
いるならば、最初にSELECTをすることでINSERTは競合しなくなりますが、
排他ロックへのアップグレードでデッドロックの発生率が上がるはずです。
というわけで、やはりこちらも再試行が必須になります。
のん吉
会議室デビュー日: 2007/07/05
投稿数: 8
投稿日時: 2008-07-24 20:02
こんばんは、のん吉です。

思いのほか盛り上がっていてビックリです。。。

やはり、B番でロックかけてですね。
@とAの話は、処理によって後書き優先でいい場合は可能?
(PostgreSQLは使った事が無かったのですがクセありますね。)

皆さん、前提が少ないなかで
ご意見頂き、有難う御座いました。

遅ればせながら、DBは
SQLServer2005を採用しています。

スキルアップ/キャリアアップ(JOB@IT)