ETLおよびさまざまなレポートシナリオでの一般的な要件は、SQL Serverステージングテーブルをバックグラウンドで静かにロードすることです。これにより、データをクエリするユーザーは書き込みの影響を受けず、その逆も同様です。秘訣は、データの新しい更新されたバージョンをユーザーにいつどのように向けるかです。
ステージングテーブルの簡略化された例:農家の市場の例え
では、SQLのステージングテーブルとは何ですか?ステージングテーブルは、実際の例を使用するとより簡単に理解できます。たとえば、地元の農家の市場で販売している野菜でいっぱいのテーブルがあるとします。野菜が売れ、新しい在庫を持ち込むと:
- たくさんの新しい野菜を持ってくると、テーブルを片付けて残りの在庫を新しい製品と交換するのに20分かかります。
- ほとんどの人が野菜を他の場所で手に入れるので、顧客がそこに座って切り替えが行われるまで20分待つことは望ましくありません。
さて、新しい野菜をロードする2番目の空のテーブルがあり、それを実行している間、顧客は最初のテーブルから古い野菜を購入できるとしたらどうでしょうか。 (古い野菜が悪くなった、またはそうでなければあまり望ましくないからではないふりをしましょう。)
SQLServerでのテーブルの更新
テーブルがアクティブに照会されている間にテーブル全体をリロードするには、いくつかの方法があります。 20年前、私はsp_rename
を無制限に利用しました —テーブルの空のシャドウコピーを使用してシェルゲームをプレイし、シャドウコピーをリロードしてから、トランザクション内で名前の変更のみを実行します。
SQL Server 2005では、スキーマを使用して、次の2つの投稿で説明したのと同じシェルゲーム手法を使用して転送したテーブルのシャドウコピーを保持するようになりました。
- トリックショット:Schema Switch-a-Roo
- Schema Switch-a-Roo、パート2
名前を変更するよりもスキーマ間でオブジェクトを転送することの唯一の利点は、オブジェクトの名前を変更することに関する警告メッセージがないことです。警告メッセージがエージェント履歴ログをはるかに速くいっぱいにすることを除いて、それ自体は問題ではありません。
どちらのアプローチでも、スキーマ変更(Sch-M)ロックが必要なため、既存のトランザクションが独自のロックを解放するのを待つ必要があります。 Sch-Mロックを取得すると、スキーマ安定性ロック(Sch-S)を必要とする後続のクエリをブロックします。これはほぼすべてのクエリです。 Sch-Sを必要とする新しいクエリは、Sch-Mの背後のキューに入れなければならないため、これは急速にブロッキングチェーンの悪夢になる可能性があります。 (いいえ、RCSIまたはNOLOCK
を使用してこれを回避することはできません それらのクエリでさえSch-Sを必要とするので、どこでも。互換性がないため、Sch-Mを配置した状態でSch-Sを取得することはできません。MichaelJ。Swartがここで説明しています。)
Kendra Littleは、彼女の投稿「ステージングデータ:ALTER SCHEMA TRANSFERによる危険のロック」で、スキーマ転送の危険性について本当に目を開かせました。そこで彼女は、スキーマの転送が名前の変更よりも悪い場合がある理由を示しています。彼女は後で、テーブルをスワップアウトする3番目の、はるかに影響の少ない方法について詳しく説明しました。これは、現在私が排他的に使用しているパーティションの切り替えです。この方法では、スイッチが低い優先度で待機できるようになります。これは、名前変更やスキーマ転送の手法ではオプションではありません。 Joe Sackは、SQLServer2014で追加されたこの拡張機能について詳しく説明しました。「SQLServer2014CTP1での優先度の低いロック待機オプションの調査」
SQLServerパーティション切り替えの例
ここでケンドラの徹底的な要点に従って、基本的な例を見てみましょう。まず、2つの新しいデータベースを作成します。
CREATE DATABASE NewWay; CREATE DATABASE OldWay; GO
新しいデータベースでは、野菜の在庫を保持するためのテーブルと、シェルゲーム用のテーブルの2つのコピーを作成します。
USE NewWay; GO CREATE TABLE dbo.Vegetables_NewWay(VegetableID int、Name sysname、WhenPicked datetime、BackStory nvarchar(max));GO-テーブルのコピーを2つ追加する必要があります。 CREATE TABLE dbo.Vegetables_NewWay_prev(VegetableID int、Name sysname、WhenPicked datetime、BackStory nvarchar(max)); GO CREATE TABLE dbo.Vegetables_NewWay_hold(VegetableID int、Name sysname、WhenPicked datetime、BackStory nvarchar(max));テーブルのステージングコピーをロードし、トランザクションを使用して現在のコピーを切り替えるプロシージャを作成します。
CREATE PROCEDURE dbo.DoTheVeggieSwap_NewWayASBEGIN SET NOCOUNT ON; TRUNCATE TABLE dbo.Vegetables_NewWay_prev; INSERT dbo.Vegetables_NewWay_prev SELECT TOP(1000000)s.session_id、o.name、s.last_successful_logon、LEFT(m.definition、500)FROM sys.dm_exec_sessions AS s CROSS JOIN model.sys.all_objects AS o INNERJOINmodel.sys。 all_sql_modules AS mONo。[object_id]=m。[object_id]; --ここでSch-Mロックを取得する必要があります:BEGIN TRANSACTION; ALTER TABLE dbo.Vegetables_NewWay SWITCH TO dbo.Vegetables_NewWay_hold WITH(WAIT_AT_LOW_PRIORITY(MAX_DURATION =1 MINUTES、ABORT_AFTER_WAIT =BLOCKERS)); ALTER TABLE dbo.Vegetables_NewWay_prev SWITCH TO dbo.Vegetables_NewWay;トランザクションのコミット; --そして今、ユーザーはdboの新しいデータをクエリします--古いコピーを元に戻して切り捨てることができます--他のクエリに干渉することなくALTER TABLE dbo.Vegetables_NewWay_hold SWITCH TO dbo.Vegetables_NewWay_prev; TRUNCATE TABLE dbo.Vegetables_NewWay_prev; ENDGO
WAIT_AT_LOW_PRIORITY
の美しさABORT_AFTER_WAIT
を使用して動作を完全に制御できますか オプション:
ABORT_AFTER_WAIT 設定 | |
---|---|
自己 | これは、スイッチがあきらめることを意味します n の後 分。 切り替えを実行しようとしているセッションの場合、これはエラーメッセージとして表示されます: ロック要求のタイムアウト期間を超えました。 |
ブロッカー | これにより、スイッチは最大n まで待機するようになります。 数分後、その前にあるすべてのブロッカーを殺して、ラインの最前線に自分自身を強制します 。 スイッチ操作によってぶつかるテーブルと対話しようとするセッションには、次のエラーメッセージの組み合わせが表示されます。 優先度の高いDDL操作のため、セッションが切断されました。セッションが強制終了状態であるため、実行を続行できません。 現在のコマンドで重大なエラーが発生しました。結果がある場合は、破棄する必要があります。 |
なし | これは、MAX_DURATION に関係なく、スイッチが順番を迎えるまで喜んで待機することを示しています。 。
これは、 |
BLOCKERS
このステージング/切り替え操作により、ユーザーが少し古くなったデータを表示しても問題ないと既に言っているため、オプションは最も使いやすい方法ではありません。 SELF
を使用したいと思います 割り当てられた時間内に必要なロックを取得できなかった場合は、操作を再試行してください。ただし、データが古くなりすぎないようにするために、失敗の頻度、特に連続した失敗を追跡します。
スキーマを切り替える古い方法との比較
以前は切り替えをどのように処理したかを次に示します。
USEOldWay;GO-テーブルの2つのスキーマと2つのコピーを作成しますCREATESCHEMAprev AUTHORIZATION dbo; GO CREATE SCHEMA hold AUTHORIZATION dbo; GO CREATE TABLE dbo.Vegetables_OldWay(VegetableID int、Name sysname、WhenPicked datetime、BackStory nvarchar( max)); GO CREATE TABLE prev.Vegetables_OldWay(VegetableID int、Name sysname、WhenPicked datetime、BackStory nvarchar(max)); GO CREATE PROCEDURE dbo.DoTheVeggieSwap_OldWayASBEGIN SET NOCOUNT ON; TRUNCATE TABLE prev.Vegetables_OldWay; INSERT prev.Vegetables_OldWay SELECT TOP(1000000)s.session_id、o.name、s.last_successful_logon、LEFT(m.definition、500)FROM sys.dm_exec_sessions AS s CROSS JOIN model.sys.all_objects AS o INNERJOINmodel.sys。 all_sql_modules AS mONo。[object_id]=m。[object_id]; --ここでSch-Mロックを取得する必要があります:BEGIN TRANSACTION; ALTERSCHEMAはTRANSFERdbo.Vegetables_OldWayを保持します。 ALTER SCHEMA dbo TRANSFER prev.Vegetables_OldWay;トランザクションのコミット; --そして今、ユーザーはdboで新しいデータをクエリします--古いコピーを転送して戻し、他のクエリに干渉することなくそれを切り捨てることができます:ALTER SCHEMA prev TRANSFER hold.Vegetables_OldWay; TRUNCATE TABLE prev.Vegetables_OldWay; ENDGO
Erik Ejlskov JensenのSQLQueryStressの2つのウィンドウを使用して同時実行テストを実行しました。1つは1分ごとにプロシージャの呼び出しを繰り返し、もう1つはこのような16のスレッドを数千回実行します。
BEGIN TRANSACTION; UPDATE TOP(1)dbo。
スキーマ転送 | ABORT_AFTER_WAIT: SELF | ABORT_AFTER_WAIT: BLOCKERS | |
---|---|---|---|
平均期間–転送/切り替え | 96.4秒 | 68.4秒 | 20.8秒 |
平均期間– DML | 18.7秒 | 2.7秒 | 2.9秒 |
例外–転送/切り替え | 0 | 0.5/分 | 0 |
例外– DML | 0 | 0 | 25.5/分 |
期間と例外カウントは、サーバーの仕様と環境内で他に何が起こっているかに大きく依存することに注意してください。また、SQLQueryStressを使用する場合、スキーマ転送テストに例外はありませんでしたが、使用するアプリケーションによっては、より厳密なタイムアウトが発生する可能性があることにも注意してください。そして、ブロッキングがはるかに積極的に積み重なっていたため、平均して非常に遅くなりました。誰も例外を望んでいませんが、このようなトレードオフがある場合は、ずっと長く待っているすべての人よりも、あちこちでいくつかの例外を好むかもしれません(更新操作の頻度によって異なります)。
SQLServerテーブルを更新するためのパーティション切り替えと名前変更/スキーマ転送
パーティションの切り替えにより、プロセスのどの部分が同時実行のコストを負担するかを選択できます。切り替えプロセスを優先して、データの信頼性を高めることができますが、これは一部のクエリが失敗することを意味します。逆に、更新プロセスが遅くなる(そして時々失敗する)という犠牲を払って、クエリに優先順位を付けることができます。主な目的は、SQL Serverパーティションの切り替えは、ほぼすべてのポイントで以前の名前変更/スキーマ転送手法と比較してSQL Serverテーブルを更新するための優れた方法であり、より堅牢な再試行ロジックを使用するか、期間の許容範囲を試してスイートスポットに到達することができますあなたのワークロードのために。