web-dev-qa-db-ja.com

複数のテーブルの現在のデータベースから新しいデータベースにデータを移動する最も速い方法は?

これが私の最初の質問と投稿なので、上達できるかどうか教えてください。あまりにも多くの情報を提供するのは間違いです。

私は会社でかなり新しいDBAであり、DBAチームに2年足らず、特定のエンティティの既存のデータベースから、まだ存在しない新しいデータベースにデータを移動する方法を考え出す必要があります。 。データベースは、SQL Server 2012 SP1 Standardエディションを実行しているインスタンス上にあります。

現在、purge-entitydataというカスタムPowerShellコマンドレットがあります。このコマンドレットは、エンティティ値に基づいて、現在のデータベースの160のユーザーテーブルすべてで削除を実行します。

purge-entitydata -entity <Entity>

ただし、現在のデータベース内のエンティティのデータをすべて削除する前に、ダウンストリームプロセスが失敗した場合に備えて、そのエンティティのデータのみを[Entity_Backup]という新しい一時データベースに移動する必要があります。

ここにいくつかの警告があります:

  1. 160のユーザーテーブルは、年に数回変更される可能性があります。一部が削除されるか、新しいものが追加される場合があります。 SSISルートに行くことを考えていましたが、新しいテーブルが追加または削除されるたびに新しいデータフロータスクでパッケージを更新する必要はありませんでした。

  2. データとテーブル自体を移動する必要があるだけです。ビュー、ストアドプロシージャ、またはインデックスを現在のデータベースから一時データベースに移動する必要はありません。

  3. 現在のデータベースとユーザーテーブルには、1,000を超えるエンティティのデータが含まれています。 1つのエンティティのデータを一時データベースに移動するだけです。

  4. ダウンストリームプロセスが成功すると、一時データベースが削除されます。

  5. データベースは大きく、200 GBを超えます。また、このサーバーはプルマージレプリケーションサブスクリプションのパブリッシャーであり、2つのトランザクションレプリケーションサブスクリプションのパブリッシャーでもあります。バックアップを取るとレプリケーションが遅くなりすぎると思います。

Sp_msforeachtable 'SELECT * INTO [Entity_Backup]を使用することを考えていました。 FROM? WHERE Entity = $ Entity 'およびInvoke-Sqlcmdですが、並行して実行されないため、これには時間がかかりすぎると心配しています。エンティティには約300〜500万行あります。

PowerShellコマンドレット内から実行できる、160のユーザーテーブルから新しいデータベースにデータを移動するための最も効率的/最速の方法は何ですか?ms_foreachtableよりも速いかもしれない提案はありますか?

3
AABCDS

あなたは、提案されたソリューションが並列に実行されないために機能しないことを心配していると言います。 160個すべてのテーブルから削除するPowerShellコマンドレットは既にありますよね?行を削除すると、削除するテーブルにインデックスがある場合は特に、行を挿入するよりも時間がかかることがよくあります。そのコマンドレットが並列で実行されない場合、なぜ挿入コマンドを並列で実行する必要があるのですか?比較的簡単なソリューションから始め、パフォーマンステストを行い、必要に応じてそれをさらに複雑にすることをお勧めします。

PowerShellで実行できるものの制限についてはよく知りません。ただし、 PowerShellからストアドプロシージャを実行する が可能であると思われるため、T-SQLソリューションを提供します。これを必要なものに簡単に変換できるはずですが、コードを「ストアドプロシージャ」と呼んでもかまいません。

データベースが約200 GBで、約1000のエントリがある場合、平均して200 MBのデータを移動するだけで済みます。テーブルに適切なインデックス(Entity列のインデックスなど)がある場合、これは間違いなく実行可能です。あなたはすでにその分析を行っており、SELECTステートメントを高速化するために必要なインデックスがあると仮定します。

1)挿入用のデータベースを作成します

シンプルなリカバリモデル を使用して一時データベースを作成することをお勧めします。これにより、最小限のログクエリでトランザクションログに書き込まれるデータが削減されます。また、データの寿命が非常に短いため、別の復旧モデルを使用してもメリットはありません。また、適切な設定でデータベースを作成し、そのままにしておくことをお勧めします。自動拡張イベントを防ぐために、データファイルとログファイルを適切なサイズに拡張します。不要になったデータベースのすべてのテーブルを削除できます。データベースを作成および削除するたびに、本当に作業を行う必要がありますか?

2)DML戦略を理解する

一時データベースにデータを挿入する方法は3つ考えられます。それらの2つは、削除アプレットの変更を必要とします。

それらはすべてヒープに挿入され、 最小限のロギング を利用します。必要なのはデータだけであり、挿入を実行するオーバーヘッドを最小限に抑えることができるからです。 SQL Server 2012では、 並列挿入 は実行できません。 SQL Server 2014にアップグレードしてSELECT INTOを使用すれば、それらを実行できます。

最初の方法では、OUTPUT句を使用してテーブルから行を削除し、削除した行を1つのクエリに挿入します。テーブルごとに1回だけ基になるデータを読み取る必要があるため、これがおそらく最も高速な方法です。 insertステートメントにSELECT INTOを使用することはできません(ただし、事前に実行してターゲットテーブルを作成することはできます)。クエリは次のようになります。

INSERT INTO [other_db]..[Entity_Table_1] WITH (TABLOCK)
SELECT t.*
FROM 
(
    DELETE FROM [source_db]..[Entity_Table_1]
    OUTPUT deleted.*
) t;

2番目の方法は最初の方法と似ていますが、削除クエリの直前にテーブルの挿入クエリを処理します。挿入クエリは、理論的には、必要なデータをテーブルからバッファキャッシュに移動します。これにより、コードが必要とするディスクへのトリップの数が減り、コードがより高速に実行されます。これには、削除アプレットのコードを変更する必要があります。

3番目の方法は、単に挿入を行うことです。この方法では、削除アプレットを変更する必要はありません。 SELECT INTOを使用しない場合は、TABLOCKヒントを使用して最小限のログを取得することをお勧めします。

3)ループの方法と順序を理解する

sp_MSforeachtableの使用はお勧めしません。これは 非文書化 であり、Microsoftによっていつでも変更または削除される可能性があります。代わりに、 sys.tables から関連するテーブルをループするために cursor を使用できます。オンラインでこれを行う方法の例はたくさんあるはずですが、さらに助けが必要な場合はお知らせください。

テーブルを処理する順序が重要になる場合があります。たとえば、deleteステートメントとinsertステートメントに別々のアプリケーションが必要であるとします。両方の手順で同じ順序でテーブルを処理する場合、現在のテーブルのデータがバッファキャッシュから押し出されてから、再度処理される可能性があります。これは、同じテーブルを再度クエリする前に、他の159のテーブルをクエリしているためです。テーブルから削除する順序とは逆の順序でテーブルを処理した場合、データを読み取るときの物理的な読み取りを減らすことができます。

4)必要に応じて並列処理を実装します

テスト後、コードが遅すぎて、複数の同時挿入ステートメントを実行する必要があることに気付く場合があります。これを行う1つの方法として、まず、ストアドプロシージャが常に確定的な順序でテーブルをループするようにします。プロシージャに2つの入力パラメータを追加します。最初はスレッドの総数で、2番目はそのプロシージャコールのスレッドである必要があります。合計4つのスレッドとアクティブスレッドを1としてプロシージャを呼び出した場合、そのプロシージャは1、5、9、13、...テーブルを処理します。 Nスレッドの場合、N PowerShellウィンドウを開いて、それぞれの2番目のパラメーターを変更できます。

ハッピー挿入!

3
Joe Obbish