SQL Server 2005データベース(行のバージョン管理なし)を使用して巨大なselectステートメントを実行しており、他のステートメントの実行をブロックしていることがわかります(sp_who2
)。 SELECTステートメントがブロッキングを引き起こす可能性があることに気付きませんでした。これを軽減するために何かできることはありますか?
SELECTは更新をブロックできます。適切に設計されたデータモデルとクエリは、最小限のブロッキングを引き起こすだけで、問題にはなりません。 「通常の」WITH NOLOCKヒントは、ほとんど常に間違った答えです。適切な答えは、巨大なテーブルをスキャンしないようにクエリを調整することです。
クエリを調整できない場合は、まず SNAPSHOT ISOLATION level を検討する必要があります。次に、 DATABASE SNAPSHOTS の使用を検討する必要があります。最後のオプションはDIRTY READSである必要があります(変更することをお勧めします- 分離レベル NOLOCK HINTを使用するのではなく)。名前が明確に示すように、ダーティリードは一貫性のないデータを返します(たとえば、シート全体のバランスが取れていない可能性があります)。
documentation から:
Shared (S)
ロックを使用すると、同時トランザクションで、悲観的同時実行制御のもとで_(SELECT)
_リソースを読み取ることができます。詳細については、_Types of Concurrency Control
_を参照してください。リソースにshared (S)
ロックが存在する間、他のトランザクションはデータを変更できません。Shared (S)
リソースのロックは、トランザクション分離レベルが反復可能読み取り以上に設定されていない限り、またはロックヒントを使用してshared (S)
を保持しない限り、読み取り操作が完了するとすぐに解放されます。トランザクションの期間中のロック。
_shared lock
_は、別の共有ロックまたは更新ロックと互換性がありますが、排他ロックとは互換性がありません。
つまり、SELECT
クエリはUPDATE
およびINSERT
クエリをブロックし、逆も同様です。
SELECT
クエリは、テーブルから値のブロックを読み取るときに一時的な共有ロックを設定し、読み取りが完了したらそれを削除します。
ロックが存在する間は、ロックされた領域のデータを使用して何かを行うことはできません。
2つのSELECT
クエリが互いにブロックすることは決してありません(それらが_SELECT FOR UPDATE
_でない限り)
データベースでSNAPSHOT
分離レベルを有効にして使用できますが、UPDATE
クエリがSELECT
クエリによってロックされるのを防ぐことはできません(これはあなたのケースのようです) )。
ただし、SELECT
クエリがUPDATE
によってロックされるのを防ぎます。
また、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/reproducing-deadlocks-involving-only-one-table.aspx
または不正な結果:
「READ COMMITTEDおよびREPEATABLE READで選択すると、誤った結果が返される場合があります。」
WITH(READPAST)
テーブルヒントを使用できます。 WITH(NOLOCK)
とは異なります。トランザクションが開始される前のデータを取得し、誰もブロックしません。トランザクションが開始される前にステートメントを実行したと想像してください。
SELECT * FROM table1 WITH (READPAST)
トランザクションレベル をコミットされていない読み取りに設定できます