web-dev-qa-db-ja.com

SQL Serverにデータを挿入すると、テーブル全体がロックされますか?

Entity Framework を使用しており、blobフィールドを含むレコードをデータベースに挿入しています。 blobフィールドには最大5MBのデータを含めることができます。

このテーブルにレコードを挿入するとき、テーブル全体をロックしますか?

テーブルのデータをクエリしている場合、挿入が完了するまでブロックされます(これを回避する方法はありますが、デフォルトで話しているのですか)。

デッドロックが発生するまでにどれくらい時間がかかりますか?その時間は、サーバーの負荷量に依存しますか。負荷が少ない場合、デッドロックが発生するまでに時間がかかりますか?

特定の時間にロックされているものを監視して確認する方法はありますか?

各スレッドが単一のテーブルでクエリを実行している場合、ブロッキングが発生する可能性がありますか?結合があり、複数のテーブルで動作しているクエリがある場合にのみ、デッドロックが発生する可能性はありませんか?

これは、私のコードの大部分が選択ステートメントの集まりであり、長時間実行されるトランザクションのヒープやそのようなものではないことを考慮しています。

48
peter

神聖な牛、あなたはここでたくさんの質問を持っていますね。ここにいくつかの答えがあります:

このテーブルにレコードを挿入するとき、テーブル全体をロックしますか?

デフォルトではありませんが、TABLOCKヒントを使用する場合、または特定の種類のバルクロード操作を行う場合は、yesです。

テーブルからデータをクエリしている場合、挿入が完了するまでブロックされます(これを回避する方法があることに気付きますが、デフォルトで話している)?

これは少し複雑になります。あなたがロックしたテーブルのページからデータを選択しようとしている場合、はい、あなたはそれらをブロックします。 selectステートメントのNOLOCKヒントなどを使用して、またはRead Committed Snapshot Isolationを使用して、この問題を回避できます。分離レベルがどのように機能するかについての開始点については、 Kendra Littleの分離レベルのポスター を確認してください。

デッドロックが発生するまでにどれくらい時間がかかりますか?その時間は、サーバーの負荷量に依存しますか。負荷があまりない場合、デッドロックを引き起こすのに時間がかかりますか?

デッドロックは時間に基づいているのではなく、依存関係に基づいています。この状況があるとしましょう:

  • クエリAは多数のロックを保持しており、クエリを完了するには、クエリBによってロックされているものが必要です。
  • クエリBも多数のロックを保持しています。クエリを完了するには、クエリAによってロックされているものが必要です。

どちらのクエリも前方に移動できないため(メキシコのスタンドオフと考えてください)、SQL Serverはそれをドローと呼び、誰かのクエリを後ろで撃ち、ロックを解除し、他のクエリを続行させます。 SQL Serverは、ロールバックするのに費用がかからない犠牲者を選択します。豪華にしたい場合は、特定のクエリでSET DEADLOCK_PRIORITY LOWを使用してターゲットをペイントし、SQL Serverが最初にそれらを撃ちます。

特定の時間にロックされているものを監視して確認する方法はありますか?

絶対に-sys.dm_tran_locksのようにクエリできる動的管理ビュー(DMV)がありますが、最も簡単な方法は Adam Machanicの無料のsp_WhoIsActiveストアドプロシージャ を使用することです。これは、sp_whoの非常に洗練された代替品であり、次のように呼び出すことができます。

sp_WhoIsActive @get_locks = 1

実行中のクエリごとに、保持するすべてのロックを記述する小さなXMLを取得します。 [ブロック]列もあるため、誰が誰をブロックしているかを確認できます。保持されているロックを解釈するには、 Books Onlineのロックタイプの説明 を確認する必要があります。

各スレッドが単一のテーブルでクエリを実行している場合、ブロッキングが発生する可能性がありますか?結合があり、複数のテーブルで動作しているクエリがある場合にのみデッドロックが発生する可能性はありませんか?

信じられないかもしれませんが、 1つのクエリが実際にそれ自体をデッドロックする可能性があります 、そして、はい、クエリは1つのテーブルだけでデッドロックすることができます。デッドロックの詳細については、 ジェレミアペシュカによるデッドロックの難易度 をご覧ください。

116
Brent Ozar

SQLを直接制御できる場合、次を使用して行レベルのロックを強制できます。

INSERT INTO MyTable(Id, BigColumn) WITH (ROWLOCK)
VALUES(...)

次の2つの答えが役立つ場合があります。

SQL Serverで行レベルのロックを強制することは可能ですか?

Entity Frameworkでselectを使用してテーブルをロックする

Management Studioで現在保持されているロックを表示するには、サーバー、[管理/アクティビティモニター]の順に確認します。オブジェクトごとのロックに関するセクションがあるため、挿入が実際に問題を引き起こしているかどうかを確認できるはずです。

3
Andrew Bain

デッドロックエラーは通常、非常に迅速に戻ります。ロックの待機中にタイムアウトエラーが発生したため、デッドロック状態は発生しません。デッドロックは、ロック要求のサイクルを探すことでSQL Serverによって検出されます。

0
Timothy Klenke

私が思いつく最良の答えは、次のとおりです。

確認する最良の方法は、接続SPIDを見つけ、sp_lock SPIDを使用して、TABモードでロックモードがXであるかどうかを確認することです。 SELECT OBJECT_NAME(objid)を使用してテーブル名を確認することもできます。また、以下のクエリを使用してロックをチェックすることも好きです。

    SELECT RESOURCE_TYPE,RESOURCE_SUBTYPE,DB_NAME(RESOURCE_DATABASE_ID) AS 'DATABASE',resource_database_id DBID,
    RESOURCE_DESCRIPTION,RESOURCE_ASSOCIATED_ENTITY_ID,REQUEST_MODE,REQUEST_SESSION_ID,
    CASE WHEN RESOURCE_TYPE = 'OBJECT' THEN OBJECT_NAME(RESOURCE_ASSOCIATED_ENTITY_ID,RESOURCE_DATABASE_ID) ELSE '' END OBJETO
    FROM SYS.DM_TRAN_LOCKS (NOLOCK)
    WHERE REQUEST_SESSION_ID = --SPID here

SQL Server 2008(およびそれ以降)では、テーブルのロックエスカレーションを無効にし、挿入句でWITH(ROWLOCK)を強制して、行ロックを効果的に強制できます。これは、SQL Server 2008より前には実行できません(WITH ROWLOCKを記述できますが、SQL Serverはこれを無視することもできます)。

私はここで将軍について話しているのですが、特に1MBを超える場合は、BLOBを避けるよう開発者に勧めるので、BLOBの経験はあまりありません。

0
Thiago Dantas