web-dev-qa-db-ja.com

インデックスが多いテーブルの一括挿入が遅い

20を超えるインデックスを持つテーブルに数百万のレコードを挿入しようとしています。

前回の実行では、100.000行あたり4時間以上かかり、クエリは3日半後にキャンセルされました...

これをスピードアップする方法について何か提案はありますか。

(原因として多くのインデックスが考えられます。そうだと思う場合は、操作の前にインデックスを自動的に削除して、後で同じインデックスを作成する方法を教えてください。)

追加情報:

  • インデックスで使用されるスペースは、データのみで使用されるスペースの約4倍です。
  • 挿入は、100.000行ごとのトランザクションでラップされます。

ステータスの更新:

受け入れられた答えは私がそれをはるかに速くするのを助けました。

26
Ole Lynge

インデックスを無効または有効にできます。それらを無効にすると、インデックスを再度有効にしたときにのみ見つかる望ましくない副作用(主キーの重複や一意のインデックスなど)が生じる可能性があることに注意してください。

--Disable Index
ALTER INDEX [IXYourIndex] ON YourTable DISABLE
GO

--Enable Index
ALTER INDEX [IXYourIndex] ON YourTable REBUILD
GO
42
Lucero

これは、データウェアハウスの操作のように聞こえます。挿入前にインデックスを削除し、後で再構築するのが普通です。

インデックスを再構築するときは、最初にクラスター化インデックスを構築し、逆に最後に削除します。それらはすべてfillfactor 100%である必要があります。

コードはこのようなものでなければなりません

if object_id('Index') is not null drop table IndexList
select name into Index from dbo.sysindexes where id = object_id('Fact')

if exists (select name from Index where name = 'id1') drop index Fact.id1
if exists (select name from Index where name = 'id2') drop index Fact.id2        
if exists (select name from Index where name = 'id3') drop index Fact.id3
.
.
BIG INSERT

RECREATE THE INDEXES
8
cindi

別の回答で指摘されているように、インデックスを無効にすることは非常に良いスタートです。

100.000行あたり4時間[...]挿入は100.000行あたりのトランザクションにラップされます。

数を減らすことを検討する必要があります。サーバーはトランザクション中に大量の状態を維持する必要があり(ロールバックできるように)、これは(インデックスとともに)データの追加が非常に困難な作業であることを意味します。

各挿入ステートメントを独自のトランザクションでラップしてみませんか?

また、使用しているSQLの性質を見てください。ステートメントごとに1行追加(およびネットワークラウンドトリップ)しますか、それとも多く追加しますか?

4
Richard

このような場合、インデックスを無効にしてから再度有効にすることをお勧めします。しかし、私はこのアプローチについて疑問を持っています。

(1)アプリケーションのDBユーザーには、通常は所有してはならないスキーマ変更特権が必要です。 (2)選択した挿入アプローチまたはインデックススキーマ、あるいはその両方は、最初は最適ではない可能性があります。そうでない場合、完全なインデックスツリーの再構築は、まともなバッチ挿入(たとえば、クライアントが一度に1つの挿入ステートメントを発行して、何千ものサーバーラウンドトリップ、またはクラスター化されたインデックスの選択が不適切であるため、インデックスノードが一定に分割されます)。

それが私の提案が少し異なって見える理由です:

  • ADO.NETBatchSizeを増やす
  • ターゲットテーブルのクラスター化インデックスを適切に選択して、挿入によってクラスター化インデックスノードが分割されないようにします。通常、ID列が適切な選択です
  • クライアントが最初に一時ヒープテーブルに挿入するようにします(ヒープテーブルにはクラスター化インデックスがありません)。次に、1つの大きな「insert-into-select」ステートメントを発行して、すべてのステージングテーブルデータを実際のターゲットテーブルにプッシュします。
  • SqlBulkCopyを適用する
  • 一括ログ復旧モデルを選択して、トランザクションログを削減する

この記事 でより詳細な情報を見つけるかもしれません。

3
DannyB