2017-02-02 3 views
0

.NET Webアプリケーションでは、2つの部分に分かれているルーチンがあります。 1をすることができ、時には他のものを呼び出さずに呼び出されるため、時間のほとんどは日常の両方の部分が次々と呼ばれ、Sql Server 2012ロック(UPDLOCK)の奇妙な問題

var result = Method1(someParams); 
var finalResult = Method2(result); 

は、それらが二つに分割されています。どちらのメソッドも内部的にテーブルTable1のデータを照会します。上記の2つのメソッドのパラメータが異なるので、それらが実行する選択クエリも異なりますが、これらの2つのメソッドのいずれかによって選択される行をロックしたいので、もう一方のメソッドはその前に選択できませんそれを最初に照会したものが完成しました。選択したデータは後で更新する必要があるため、selectクエリではUPDLOCKというキーワードを使用しますが、上記のように2つのメソッドのパラメータが異なるため、最良の結果を得るために行ったのは次のとおりです。

Method1クエリ

次に、Idを選択して次のクエリを実行して、すべての列を取得します。 (idが外部からそれに渡されるように)

SELECT Id, Column1, Column2, Column3, Column4 
FROM Table1 WITH (UPDLOCK, INDEX (PK_Table1)) 
WHERE Id = @IdParam 

Method2は、2番目のクエリを実行します。したがって、同じレコードに対してMethod1Method2が同時に呼び出されると、同じ行に対してUPDLOCKを取得する必要があるデータを更新する前に、別のレコードを待つことになります。

私はデータベースが何らかの理由でインデックスを切り替えていると考えていたので、クエリにはINDEX(PK_Table1) --(Primary Key)も含まれていました。

私が時折実行する問題は、これらのメソッドが同時に(同じ行に対して)4回以上呼び出されたときに、ロック機構が正しく機能しないことです。レコードが選択されたときにロックされていないことを直接意味する結果が表示されます。このような場合には、上記のクエリを含むプロシージャを変更するだけです(クエリを変更せずに、おそらく再コンパイルする必要があります)。上記のメカニズムが正しく動作するかどうかをチェックするテストが書かれているので、変更が役立つことがわかります。問題があることを検出した後、プロシージャを変更する前に、誤った結果をもたらすテストを実行し、ロックが正しく機能していない場合は発生しないはずです。これら2つのプロシージャを変更した後、テストを再実行しても問題ありません。

このケースは1〜2ヶ月に1回出現しますが、検出されて修正される前に実際にはダメージを受けます。それはすでに4回起こっています。 3回目に発生したときは、WITH句にINDEX(PK_Table1)を追加しました。これは修正であると確信していましたが、もう一度起こりました。

+0

私はこれを再現できるかどうか試してみることに興味があります。しかし、SQL Serverがバグを起こす可能性は低いですが、SPはトランザクションなしでどこかで呼び出されている可能性が低いと思わなければなりません。 あなたの最後の返信から、あなたの.netコードでトランザクションをコミットしているという私の悪い回答に変えますか?その場合は、トランザクション管理をSPコードに移行することを検討しましたか? –

+0

SQL 2012のビルド、データベースが設定されている互換性レベル、データベースでreadcommittedsnapshotおよび/またはスナップショットの分離が有効になっているかどうか、およびトランザクションで使用する分離レベルを尋ねることはできますか? –

+0

はいアプリケーションは.NET環境で実行されています。正確には、ASP.NET MVC 4です。これらのSQLプロシージャはMethod1とMethod2から実行され、他の誰もその存在を気にすることはありません。問題はトランザクションのオープン/コミットであることが非常に疑わしいです。 (.NET側) – Dimitri

答えて

0

(更新:OPが期待するように、異なるトランザクション内UPDLOCKが実際に動作するはずですので、ここでの私の答えは、適用されません)

UPDLOCKが更新のみから、読んでから他のセッションを防ぐことはできません。実際には、ヒントや分離レベルを使用してこれを回避する方法はありません。

ロックを生成するために現在の値で実際に行を更新し、トランザクションをコミットする前に新しい値で再度更新することで、実行しようとしていることを達成できます。しかし、あなたが目指していることをやるより安全な方法があるかもしれません。

+0

私が知っている限り、2つのUPDLOCKSが同じ行で要求されると、それは一度に1つ取得されます。最初のSELECTが実際にUPDLOCKで行を選択するとき、UPDLOCKでの別の選択は、最初のトランザクションがコミットされる前に同じ行のロックを読み取ったり取得したりすることができません。あなたはUDPLOCKが他のセッションの読み込みを妨げないことを言いますが、これは正しいことですが、UPDLOCKで読み込んでいる場合に他のセッションの読み込みを妨げます。私の場合、両方のクエリはUPDLOCKでデータを読み込んでいます。 – Dimitri

+0

私はひどく恥ずかしいです...私はあなたがたまに起こるような証拠の誤解に陥ったと思っていました。まれなSQL Serverのバグを犯した可能性は確かです。 2つのストアドプロシージャ内でトランザクションを開いてコミットしていますか? –

+0

はい、それらのquiresとそれに続く更新はすべて、更新後にコミットされた同じSQLトランザクションスコープで実行されます。外部の2つのメソッドは同じSQLトランザクションで実行されません(次々と実行されます)が、両方のメソッド(Method1とMethod2)の内部はあります。 – Dimitri