目標:
ダウンタイムがゼロの3つのテーブルから古いレコード(約600GB)を数十億レコード削除します。
方法論と背景:
100万のMyIDに関連付けられたレコードのバッチを一度に削除する予定です(つまり、MyID BETWEEN 1 AND 1000000)。削除の実行中、2つのテーブルは非常に高温になりますが、3番目のテーブルのアクティビティは安全に中断できます。 2つのホットテーブルでは、MyIDがクラスタリングキーです。 3番目のコールドテーブルでは、MyIDに非クラスター化インデックスがあります。
DELETE操作の他に、ホットテーブルの他のアクティビティは、おそらく1秒あたり数回のINSERTで構成されます。 MyIDはIDENTITYであり、新しく挿入されたレコードはどのDELETEバッチの範囲にも含まれません。
潜在的なパフォーマンスの理解に役立つ場合、これらの行のサンプルバッチの集約のSELECTは、MyIDによってクラスター化されたテーブルでは1秒未満、非クラスター化テーブルでは約2秒かかります。この特定のデータベースを再生するための製品レプリカがないので、削除にかかる時間はわかりませんが、これをDEVにデプロイすると、より良いアイデアが得られます。
補足:ログサイズに対処するために、これらのバッチをトランザクションにラップし、TLogバックアップの頻度を15分から5分に増やしています。 150GBの利用可能なログ領域があります。
質問
私はMSドキュメントを読みました:
デフォルトでは、DELETEステートメントは常に、変更するテーブルの排他(X)ロックを取得し、トランザクションが完了するまでそのロックを保持します。
ロックヒントの使用には非常に注意していますが、この場合、TABLOCKXを回避するために安全にヒントを使用できますか?ロックヒント以外に、TABLOCKXを回避する方法はありますか?
数十億レコードの古いレコードを削除(約600GB)
この場合、削除するよりも残すべき行をコピーする方が便利な場合があります。すべての600Gbが確実にログファイルに移動することに注意してください。
デフォルトでは、DELETEステートメントは常に、変更するテーブルの排他(X)ロックを取得し、トランザクションが完了するまでそのロックを保持します。
これは間違っています。 SQL Serverは、対応するヒントが指定されていない限り、table lock
で始まることはありません。
デフォルトでは、SQL Serverは最大の同時実行性を実現するために、可能な限り細かいロックを取得します。ほとんどの場合、これはSQL Serverが行(RIDまたはKEY)ロックを取得することを意味します。 SQL Serverは、問題を引き起こすことなく、単一のテーブル内のデータに対して数百または数千の個別のロックを取得できます。ただし、SQL Serverがクエリがクラスター化インデックス内の行の範囲にアクセスすると判断した場合は、代わりにページロックを取得することがあります。結局のところ、ページ上のすべての行にアクセスする場合、数十または数百の行ロックよりも単一のページロックを管理する方が簡単です。その他の場合、主にクエリの処理に役立つ使用可能なインデックスがない場合、SQL Serverはクエリの処理の開始時にテーブル全体をロックすることがあります。
PK
範囲でフィルタリングする場合:MyID BETWEEN 1 AND 1000000
それはおそらくpage
ロックで始まるでしょう。そして、ここでのすべては、1つのpage
に収まるレコードの数に依存します。 page
の粒度で1000000
レコードのロックにかかる時間が5000
ロックよりも少ない場合、page
ロックを使用すると問題ありません。ステートメントごとにlocks
が増えると、lock escalation
が発生し、テーブルは完全にロックされます。
...
インスタンス全体のしきい値を超えた場合のロックのエスカレーションに加えて、SQL Serverは、個々のセッションが1つのステートメントで5,000を超えるロックを取得した場合にもロックをエスカレートします。この場合、ロックをエスカレートするセッションを選択する際にランダム性はありません。ロックを取得したセッションです。
SQL Serverの同時実行性:K.Delaneyによるロック、ブロック、行のバージョン管理
したがって、取得したロックをテストおよび監視することによってのみ、最適なbatch size
を見つけ、lock escalation
を回避できます。テーブルのページあたりの行の平均がわかっている場合は、おおよそbatch size
を計算できます。または、PAGLOCK
を使用してページロックを強制し、テーブルのロックエスカレーションを無効にすることもできます。
ALTER TABLE MyTable SET ( LOCK_ESCALATION = DISABLE )