6

一時テーブルの値を選択し、次のようにデータベースに挿入するストアドプロシージャを実行しています。

 INSERT INTO emails (EmailAddress)   (
     SELECT
       DISTINCT eit.EmailAddress
     FROM #EmailInfoTemp eit
       LEFT JOIN emails ea
         ON eit.EmailAddress = ea.EmailAddress
     WHERE ea.EmailAddressID IS NULL   )

まれに(1分間に数千のリクエストを処理するサーバーで数時間に1回)、EmailAddress列のインデックスに一意の制約エラー「ViolationofUNIQUEKEYconstraint」が表示されます。

重複した値を渡していないことを確認できます。私がいたとしても、それはDISTINCTによって捕らえられるべきです。

-SQL Server2008-ストアドプロシージャ+トランザクションを使用しない+JDBCcallablestatement

SELECTとそれに続くINSERTの間に、同様のデータでINSERTを完了した同じ/異なるストアドプロシージャへの別の呼び出しがあった可能性がありますか?もしそうなら、それを防ぐための最良の方法は何でしょうか?

いくつかのアイデア:本番環境でこの1つのSQL Serverと一度に通信する「クライアント」の重複インスタンスが多数あるため、最初の反応は同時実行の問題でしたが、自分で複製することはできないようです。それは私が持っていた最良の推測ですが、それは今のところどこにも行きません。これは、本番環境と比較して負荷がわずかであるステージング環境では発生しません。それが、私が並行性の問題を調査し始めた主な理由でした。

4

1 に答える 1

5

このエラーは、2つのセッションが同時に挿入を実行したことが原因である可能性があります。

MERGEを使用すると、SQLコードをより安全にすることができます。Aaron Bertrandのコメントが言うように(ありがとう!)、本当に安全にするためのヒントを含める必要がありwith (holdlock)mergeます。

; merge emails e with (holdlock)
using   #EmailInfoTemp eit
on      e.EmailAddress = eit.EmailAddress
when    not matched then insert
        (EmailAddress) values (eit.EmailAddress)

mergeステートメントは適切なロックを取り、「一致しない」チェックと「挿入」の間に他のセッションが侵入できないようにします。

を使用できない場合はmerge、クライアント側で問題を解決できます。2つのインサートが同時に実行されていないことを確認してください。これは通常、ミューテックスまたは他の同期構造を使用して簡単に実行できます。

于 2013-03-13T14:52:21.913 に答える