ブロックを引き起こすSQL Server SELECTステートメント
-
06-07-2019 - |
質問
SQL Server 2005データベース(行バージョン管理なし)で巨大なselectステートメントを使用しており、他のステートメントの実行をブロックしていることがわかります( sp_who2
を使用して表示)。 SELECTステートメントがブロッキングを引き起こす可能性があることを知りませんでした-これを軽減するためにできることはありますか?
解決
SELECTは更新をブロックできます。適切に設計されたデータモデルとクエリは、最小限のブロッキングのみを引き起こし、問題にはなりません。 「通常の」WITH NOLOCKヒントは、ほとんど常に間違った答えです。適切な答えは、巨大なテーブルをスキャンしないようにクエリを調整することです。
クエリが調整できない場合は、まずスナップショット分離レベル、2番目に DATABASE SNAPSHOTS の使用を検討し、最後のオプションをDIRTYにする必要があります読み取り(および分離レベルを変更することをお勧めしますNOLOCK HINTを使用する代わりに)。名前が明確に示すように、ダーティリードは一貫性のないデータを返します(たとえば、シート全体のバランスが崩れている可能性があります)。
他のヒント
ドキュメント から:
共有(S)
ロックにより、同時トランザクションがペシミスティックな同時実行制御下でリソースを(SELECT)
読み取ることができます。詳細については、同時実行制御の種類
を参照してください。リソースにshared(S)
ロックが存在している間、他のトランザクションはデータを変更できません。リソースのShared(S)
ロックは、トランザクション分離レベルが繰り返し読み取り以上に設定されていない場合、またはを保持するためにロックヒントが使用されていない限り、読み取り操作が完了するとすぐに解放されます共有(S)
はトランザクションの間ロックします。
共有ロック
は、別の共有ロックまたは更新ロックと互換性がありますが、排他的ロックとは互換性がありません。
つまり、 SELECT
クエリは UPDATE
および INSERT
クエリをブロックし、その逆も同様です。
SELECT
クエリは、テーブルから値のブロックを読み取るときに一時的な共有ロックを設定し、読み取りが完了したら削除します。
ロックが存在する間は、ロックされた領域のデータを使用して何もできません。
2つの SELECT
クエリが互いにブロックすることはありません( SELECT FOR UPDATE
でない限り)
データベースで SNAPSHOT
分離レベルを有効にして使用できますが、 SELECT
によって UPDATE
クエリがロックされるのを防ぐことはできません。 >クエリ(あなたの場合のようです)。
ただし、 UPDATE
によって SELECT
クエリがロックされるのを防ぎます。
また、 Oracle
とは異なり、 SQL Server
はロックマネージャーを使用し、メモリ内のリンクリストにロックを保持します。
これは、リンクリスト自体がトランザクションスレッドによってロックされる必要があるため、負荷が高い場合、ロックの配置および削除の単なる事実が遅いことを意味します。
ダーティリードを実行するには、次のいずれかを実行できます。
using (new TransactionScope(TransactionScopeOption.Required,
new TransactionOptions {
IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted }))
{
//Your code here
}
または
SelectCommand = "SELECT * FROM Table1 WITH (NOLOCK) INNER JOIN Table2 WITH (NOLOCK) ..."
ダーティリードするテーブルごとにWITH(NOLOCK)を書き込む必要があることを忘れないでください
トランザクションレベルを設定できますコミットされていない
デッドロックも発生する場合があります:
" 1つのテーブルのみが関与するデッドロック" http:// sqlblog.com/blogs/alexander_kuznetsov/archive/2009/01/01/reproduce-deadlocks-involving-only-one-table.aspx
およびまたは誤った結果:
" READ COMMITTEDおよびREPEATABLE READで選択すると、誤った結果が返される可能性があります。"
WITH(READPAST)
テーブルヒントを使用できます。 WITH(NOLOCK)
とは異なります。トランザクションが開始される前にデータを取得し、誰もブロックしません。トランザクションが開始される前にステートメントを実行したと想像してください。
SELECT * FROM table1 WITH (READPAST)