MS SQL Serverでテーブル全体をロックしないようにSQLの挿入や更新を行う方法


13

DBの仕事を始めたばかりなので、基本的な質問に対する忍耐に感謝してください。ローカルマシンでSQL Server 2014を実行しています。小さなテーブルと、さまざまなアプローチをテストするための基本的なクライアントアプリケーションがあります。INSERT INTOand UPDATEステートメントの両方でテーブルロックのように見えるものを取得しています。クライアントは、次のコードを持つASP.NETアプリケーションです。

OleDbConnection cn = new OleDbConnection("Provider=SQLNCLI11; server=localhost\\SQLEXPRESS; Database=<my db>; user id=<my uid>; password=<my pwd>");
cn.Open();
OleDbTransaction tn = cn.BeginTransaction();
OleDbCommand cmd = new OleDbCommand("INSERT INTO LAYOUTSv2 (LAYOUTS_name_t, LAYOUTS_enabled_b, LAYOUTS_data_m) VALUES ('name', '-1', 'data')", cn, tn);
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT SCOPE_IDENTITY()";
int newkey = Decimal.ToInt32((decimal)cmd.ExecuteScalar());
Console.WriteLine("Created index " + newkey);
Thread.Sleep(15000);
tn.Commit();
tn = cn.BeginTransaction();
cmd.CommandText = "UDPATE LAYOUTSv2 SET LAYOUTS_enabled_b='-3' WHERE LAYOUTS_key='" + newkey + "'";
cmd.Transaction = tn;
cmd.ExecuteNonQuery();
Console.WriteLine("updated row");
Thread.Sleep(15000);
tn.Rollback();
cn.Close();

このコードを実行してから、実行する管理スタジオからSELECT * FROM LAYOUTSv2。クライアントスレッドが一時停止する両方の場合(つまり、コミット/ロールバックの前)、コミット/ロールバックが発生するまでSELECTクエリがハングします。

このテーブルには、主キーとして割り当てられたLAYOUTS_keyフィールドがあります。プロパティウィンドウでは、ページロックと行ロックの両方が許可された、一意でクラスター化されていることが表示されます。テーブルのロックエスカレーション設定は無効です...テーブルとAUTOの他の利用可能な設定を変更せずに試しました。私は試しましSELECT ... WITH (NOLOCK)たが、すぐに結果を返しますが、ここや他の場所で十分に注意さているように、私がすべきことではありません。and ステートメントのROWLOCK両方にヒントを付けてみましたが、何も変わっていません。 INSERTUPDATE

私が探している動作はこれです:のコミットの前にINSERT、他のスレッドからのクエリはINSERT編集されている行を除くすべての行を読み取ります。UPDATE他のスレッドからのクエリのコミットの前に、UPDATEed されている行の開始バージョンを読み取ります。これを行う方法はありますか?ユースケースを明確にするために他の情報を提供する必要がある場合は、お知らせください。ありがとう。


3
ちなみに、WHERE LAYOUTS_key='" + newkey + "'SQLインジェクションを含むさまざまな理由により、完全なno-noは、パラメーター化されたクエリを使用する必要があります。
マーティンスミス

1
@MartinSmithこの件についてのヘッズアップに感謝します。パラメータ化されたクエリやSQLインジェクション攻撃について聞いたことはありません。
ジョンリール

@ JohnRiehl、re:インジェクション攻撃、ユーザーnewkeyが「something';DELETE FROM LAYOUTSv2 --」に設定した場合を想像してください。ユーザーがアポストロフィを挿入してクエリを操作したため、更新は正常に完了し、テーブルが空になります。通常、パラメーター化されたクエリはのようなものになります。その後、コード内の(パラメーター)にUDPATE LAYOUTSv2 SET LAYOUTS_enabled_b='-3' WHERE LAYOUTS_key=?値を個別に割り当てます?
ダニエルハットマッハー16年

回答:


10

「テーブル全体」をロックしていない可能性があります。

テーブルの行をロックしていますが、テーブルSELECT * FROM LAYOUTSv2全体を読み取ろうとするため、必然的にそのロックによってブロックされます。

挿入の場合はREADPAST、ロックされた行をスキップするためのヒントを指定するだけです。ただし、UPDATEケースに希望する結果が得られません(行の最初のバージョンを読み取らずに行をスキップします)。

あなたがデータベースを設定した場合、読み取りコミットスナップショット分離、これは(のより一層の活用を犠牲にして、両方のケースのため、ご希望の効果が得られますtempdb


「Is Read Committed Snapshot On」をTrueに変更しましたが、ヒントは不要で完全に機能します。ありがとう!1つのフォローアップ...「スナップショットの分離を許可」をFalseに設定したままにしました...それでよろしいですか?ありがとう。
ジョンリール

@JohnRiehl- SNAPSHOTアイソレーションを明示的に使用せずに無効にし、その後これがあなたに役立つと判断した場合に有効にする場合は、明示的に使用します。
マーティンスミス

7

insertおよびupdateステートメントは、行レベルのロックを作成することになっています。ただし、トランザクションのロック数が5,000以上の場合、ロックエスカレーションが発生し、テーブルレベルのロックが作成されます。下記を参照してください。

https://technet.microsoft.com/en-us/library/ms184286(v=sql.105).aspx


INSERT文およびUPDATE文のように、この質問に関連していないが、単一の行を書いている
マーティン・スミス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.