2016-08-29 34 views
3

私のデータベースにアクセスするには、以下のコード(簡略化されたコード)があります。私は文とパラメータの使用について知っていますが、私はこれを短くして問題に集中します。第二部でSQLトランザクション内のデッドロックの回避

string ConnectionString = "ConnectionStringHere"; 

//1. insert Transaction 
SqlConnection myConnection = new SqlConnection(ConnectionString);    
SqlTransaction myTrans = myConnection.BeginTransaction();     
string sUpdate = "INSERT INTO User (FirstName, LastName) VALUES ('jon','doe')"; 
SqlCommand myCommand = new SqlCommand(sUpdate, myConnection, myTrans); 
myCommand.ExecuteNonQuery(); 

//2. select from the same table 
SqlConnection myConnection2 = new SqlConnection(ConnectionString); 
string sSelect = "SELECT FirstName, LastName FROM User WHERE ID = 123"; 
DataTable dtResult = new DataTable(); 
SqlDataAdapter myDataAdapter2 = new SqlDataAdapter(sSelect, myConnection2); 
myDataAdapter2.Fill(dtResult); //TimeOut Exception here 

//submit changes from my transaction here 
myTrans.Commit(); 

私はそのあとで私のトランザクションmyTrans.Commit();をコミットするまで、私は私のUserテーブルにアクセスすることはできませんので、私はTimeOutExcetionを取得する - デッドロック。

私の質問はここです - ここではデッドロックを避けるためのベストプラクティスは何ですか?私は、トランザクションのSELECT一部を行うことにより、またはIsolationLevel

SqlTransaction myTrans = myConnection.BeginTransaction(IsolationLevel.ReadUncommitted); 

を設定することにより、例外を避けることができしかし、私はそれらの使用法についてはよく分かりません。私は常にIDで選択するので、無効なデータについて心配する必要はありません。

+0

あなたは現在接続を開いていませんが、実際のコードですか? –

+0

@Tim、実際のコードはありません – fubo

+1

別の接続を使用しているために、現在の問題が発生します。それはあなたの必要条件ですか? – Arvo

答えて

2

デッドロックやタイムアウトの問題を解決するためにトランザクションの一部としてSELECTクエリを作成する理由はありません。あなたが考えている最初のSQL接続myConnectionReadUncommitted分離レベルを設定することも適切な方法ではありません。

  1. 最初のソリューション:あなたが助けにはなりません開始しているトランザクションmyTransに設定分離レベルIsolationLevel.ReadUncommitted私は2つの可能な解決策がある参照してください。汚れた読み込みに慣れている場合は、実際にテーブルにクエリselectを発行するために確立する2番目のSQL接続myConnection2にこの分離レベルを設定する必要があります。 selectクエリの分離レベルをmyConnection2で設定するには、with (nolock)テーブルレベルのヒントを使用する必要があります。クエリは次のようになります。

    string sSelect = "SELECT FirstName, LastName FROM User WITH (NOLOCK) WHERE ID = 123"; 
    

    詳細はhereです。 また、汚れたリードhereの結果についても読んでください。これは、この特定の分離レベルを使用することの副作用です。

  2. 第2の解決策:SQL Serverのデフォルト分離レベルはRead Committedです。したがって、myConnection2という名前の変数を使用して新しいSQL接続を介してクエリを起動すると、ReadCommitted分離レベルで作業しています。 ReadCommitted隔離レベルによって示されるデフォルトの動作はBlocking Readです。つまり、テーブルにコミットされていない変更(アクティブなトランザクションのためにコミットまたはロールバックできる)がある場合、Userテーブルのselectステートメントはブロックされます。トランザクションが完了するのを待って、ロールバックの場合に新しく更新されたデータまたは元のデータを読み取ることができます。このようなブロッキング読み取りを行わないと、データベースでよく知られている同時実行の問題であるdirty readが終了します。
    SELECTステートメントがブロックされずに行の最後にコミットされた値を使用し続ける場合は、SQLサーバーに新しいデータベースレベルの設定がREAD_COMMITTED_SNAPSHOTとなります。彼の記事hereからPinalデイブを引用

    ALTER DATABASE MyDatabase SET READ_COMMITTED_SNAPSHOT ON 
    

    :ここでは、SQLスクリプトを使用してそれを変更することができる方法であるあなたは、読者(SELECT)と 作家間のブロッキングに問題がある場合

(INSERT/UPDATE/DELETE)、アプリケーションから何も変更せずにこのプロパティー を有効にすることができます。つまり、 アプリケーションは依然として読み取りコミットされた分離で実行され、 は依然としてコミットされたデータのみを読み取ります。

:これは、データベース・レベルの設定であり、READCOMMITTED分離レベルを使用して、データベース上のすべてのトランザクションに影響します。

私の意見では、最初の解決策を検討する必要があります。また、SQL Serverクエリのデッドロックを回避するために留意すべき重要な点はほとんどありません。 Pinal Daveを引用する:here

  • トランザクションとトランザクションの時間を最小限に抑えます。
  • アプリケーションで毎回常に同じ順序でサーバーオブジェクトにアクセスします。
  • 実行中にユーザーの入力が必要なカーソル、ループ、またはプロセスを避けます。
  • アプリケーションのロック時間を短縮します。
  • 可能であればロックを防止するためにクエリヒントを使用します(NoLock、RowLock)
  • SET DEADLOCK_PRIORITYを使用してデッドロック犠牲者を選択します。
2

その選択に必要なものによって異なります。

selectの前に挿入が行われていることを確認する必要がある場合は、両方のコマンドを同じトランザクション内で実行するか、SELECTクエリを実行する前に挿入をコミットする必要があります。

ない場合は、このことSQL Serverは、あなたが言ったようにやるとUNCOMMITED READする分離レベルを設定するか、NOLOCK table hintを使用することができる可能性のいずれか、すなわち:

string sSelect = "SELECT FirstName, LastName FROM User WITH(NOLOCK) WHERE ID = 123"; 

をしかし、あなたがする決定する前にSQL Serverは、デフォルトでrowlocksを使用すると、あなたがデッドロックになるだろう、なぜ

When should you use "with (nolock)"

2

が、私はわからないんだけど、あなたが好きであろうと、それは明らかではありません。それを行う、この他の質問では、その長所と短所についてお読みください。 t o変更した行と同じ行を読み込みます。

しかし、これは、読み込みコミットされたスナップショットアイソレーション(RCSI)やスナップショットアイソレーション(スナップショット)などのオプティミスティックな並行性レベルを使用するオプションです。これは、元のテーブルのデータが現在ロックされている場合に、読者がアクセスできる行バージョンを格納するためにtempdbを使用します。 スナップショットでは、各トランザクションでトランザクション分離レベルを明示的に設定して使用する必要があります.... RCSIでは、データベースレベルの設定です。このトピックの詳細についてはthis articleをご覧ください。

関連する問題