4

私はクエリで大きなINSERTの問題を見てきました:

INSERT table_name
SELECT .....;

テーブルにはインデックスがなく、テーブルに挿入するには約2,000万行が必要です。サーバーの1つでSQLServer2008R2でクエリを実行します。元のパフォーマンスは約40分です。INSERT次に、ここで投稿を読んで、BEGIN TRANSACTION/でラップするように指示しますCOMMIT。私はそれをしました、そして費やされた時間は6分に落ちました。

TRANSACTIONただし、トランザクションラップクエリを次の数回実行しようとすると、効果がなくなったように、時間が40分に戻りました。次の実行で何が起こったのかわかりません。何か案が?

追加:

もう1つの投稿によると、TRANSACTIONはパフォーマンスではなくデータの一貫性を保つために使用されることを意図しており、5K行ごとにバッチ挿入することを提案しています。上記の単一のINSERTSELECTステートメントをバッチに分割するにはどうすればよいですか?私は混乱しています。

アップデート:

実際、パフォーマンスの向上はTRANSACTIONによるものではなく、サーバー側のテーブルキャッシングによるものである可能性があります。これは、次の数回実行すると、パフォーマンスが5分程度になるためです。

4

5 に答える 5

1

トランザクションで多くのステートメントをラップする場合INSERT ... VALUES、挿入のたびにダーティデータページをディスクに書き込む必要がないため、大幅なスピードアップが見込まれます。ただし、シングルを明示的なトランザクションでラップする場合、INSERT ... SELECT以前から暗黙的なトランザクションがあり、メカニズムが実際に変更されていないため、スピードアップはありません。おそらく、環境内で同時に何か他のものが変更された可能性があります。

パフォーマンスが徐々に低下するのは、おそらく、ターゲットテーブルが大きくなるか、その結果としてデータベースが大きくなるためです。前者は成長を止めることはなく、後者はデータベースが成長し続けるにつれて少し変動/予測不可能になる可能性があるため、おそらく低下ではなく、トレンドです。

空のテーブルにデータを確実に挿入できる場合は、より過激になり、毎回データを削除することを検討してください。SELECT INTOの代わりに使用してくださいINSERT ... SELECT。これは、参照整合性のニーズに対応できる場合と機能しない場合があります。異なる構文の利点は、異なるロギング戦略です。

次の挿入の前にテーブルを削除できないが、INSERT操作中に他の接続からテーブルにアクセスされないようにすることができる場合は、分離レベルまたはテーブルヒントを使用してロックを回避できます。ただし、同様の目標に対するはるかに安全な方法がTABLOCKヒントです。このヒントは、最初にテーブル全体をロックすることで、正反対になります。他のすべての人は除外され、行レベルのロックに時間を費やすことはありません。

ターゲットテーブルの(クラスター化された)主キーでソートされたデータを挿入します。中に他のインデックスを一時的に無効にすることを検討することもできますINSERTが、同時トラフィックが存在する場合はそれをひどく傷つける別の方法であるため、この方法を軽視しないでください。

mdfファイルのサイズに注意してください。少しずつ自動的に大きくなるような状況は避けてください。

最後の手段:ハードウェア使用率の計画を行い、ターゲットテーブルを分割します。このためには、「もっと速くしてください」という考え方から「この速度を正確に達成する必要がある」という考え方に切り替える必要があります。これは、保守が非常に複雑です。

于 2012-10-23T13:58:52.223 に答える
1

これに影響を与える可能性のある多くのことが考えられます。使用されているロギングのタイプ、サーバーで他に何が起こっているか、テーブル、ハードウェア (主にディスク IO)、テーブルに既に存在するインデックスなどで排他ロックを取得できる操作です。

2,000 万レコードを挿入すると、大量のログが生成されます。最小限のログ記録操作を実行していることを確認する必要があります。このためには、SELECT INTO を検討してください (可能な場合)。ただし、INSERT SELECT に行き詰まっている場合は、SELECT INTO を最小限のログ記録操作にできるようにする要因を検討してください。http://msdn.microsoft.com/en-us/library/dd425070%28v=sql.100%29.aspxを参照してください。

于 2012-10-23T14:25:48.360 に答える
0

これが通常のタスクである場合、SSISパッケージを作成して、これを実行する必要があるときはいつでも実行できない理由を実行する必要があります。

于 2012-10-23T13:08:04.947 に答える
0

バルクデータをすばやく挿入するには、バルクコピーを使用します。

BCPユーティリティを使用できます。

または、.Netから、またはSqlNativeClientを使用できます。

または、データベース内でバッチタイプのコピーを実行するには、次のようにします。

declare @todo table( primaryKeyFieldName primary key)
insert @todo select primaryKeyFieldName from SourceTable

declare @batch table(primaryKeyFieldName primary key)
delete @batch
while exists (select 1 from @todo)
begin 
    insert @batch select top 500 primaryKeyFieldName from @todo
    delete todo from @todo todo inner join @batch b on b.primaryKeyFieldName = todo.primaryKeyFieldName 
    insert DestinationTable(fields....)
    select s.fields, ....
    from SourceTable s inner join @batch b on s.primaryKeyFieldName = b.primaryKeyFieldName
end
于 2012-10-23T13:06:09.290 に答える
0

「WITH (TABLOCK)」ヒントでテストしたところ、最終的に満足のいくパフォーマンスが得られました。クエリ全体は、元の 40 分間と比較して 3 分間実行されます。これは大きな改善であり、クエリが最初のテーブル作成作業を行っているため、アクセスの競合について心配する必要はありません。

皆様、有益なコメントをありがとうございます。

于 2012-10-23T15:25:19.923 に答える