web-dev-qa-db-ja.com

SSISがディレクトリ内の多数のファイルを列挙してインポートするのが遅いのはなぜですか?

私はひどく遅いSSISパッケージを持っています。 1つのファイルで非常に高速で、100ファイル以下でかなり高速です。 (ファイルごとに約1秒)

ただし、私のディレクトリに数千の(非常に小さい)ファイルがある場合、プロセスは非常にゆっくりとドラッグします。私の好みは、このプロセスを営業時間後にのみ実行することですが、それまで待つことにより、インポートするフラットファイルの数は数千に達します。

パッケージは非常にシンプルです:

  • 外部ループはFor Every(ファイル列挙、ファイルパスを変数に読み込む)
  • 内部では、データを変換せずにインポートするだけです

それだ。

数千のファイルのパフォーマンスは、各ファイルで15秒以上実行されています。 UI(ステータス)の描画/スクロールが非常に遅いため、どこにあるのかさえわかりません-スタンプされた時間は、18時間前に開始された実行で15時間以上経過しています。

バージョン:MSSQL 2012

7
Chris Adragna

UI /デバッガの制限に直面していると思います。

MakeAllTheFilesとReadAllTheFilesの2つのパッケージを作成しました

MakeAllTheFilesは、作成するファイルの数を入力として受け入れます。疑似ランダム関数を使用して、データをいくつか(7)のサブフォルダーに分散します。

MakeAllTheFiles

    public void Main()
    {
        int NumberOfFilesToGenerate = (Int32)Dts.Variables["User::FilesToGenerate"].Value;
        string baseFolder = Dts.Variables["User::FolderInput"].Value.ToString();
        System.Random Rand = null;
        int fileRows = 0;
        DateTime current = DateTime.Now;
        int currentRandom = -1;
        int seed = 0;
        string folder = string.Empty;
        string currentFile = string.Empty;

        for (int i = 0; i < NumberOfFilesToGenerate; i++)
        {
            seed = i * current.Month * current.Day * current.Hour * current.Minute * current.Second;
            Rand = new Random(seed);
            currentRandom = Rand.Next();

            // Create files in sub folders
            folder = System.IO.Path.Combine(baseFolder, string.Format("f_{0}", currentRandom % 7));

            // Create the folder if it does not exist
            if (!System.IO.Directory.Exists(folder))
            {
                System.IO.Directory.CreateDirectory(folder);
            }

            currentFile = System.IO.Path.Combine(folder, string.Format("input_{0}.txt", currentRandom));

            System.IO.FileInfo f = new FileInfo(currentFile);
            using (System.IO.StreamWriter writer = f.CreateText())
            {
                int upperBound = Rand.Next(50);
                for (int row = 0; row < upperBound; row++)
                {
                    if (row == 0)
                    {
                        writer.WriteLine(string.Format("{0}|{1}", "Col1", "Col2"));                        }

                    writer.WriteLine(string.Format("{0}|{1}", row, seed));
                }
            }
            ;
        }
        Dts.TaskResult = (int)ScriptResults.Success;
    }

ReadAllTheFiles

したがって、パッケージの全体的な外観は

Read All The Files!

2つの接続マネージャーが定義されています。1つはデータベースに対するもので、もう1つは、変数@[User::CurrentFileName]を使用するように、ConnectionStringプロパティの式を持つフラットファイルに対するものです。

変数、たくさんの変数が好きなのでたくさんあります。

enter image description here

SQL実行タスクは、書き込み用のテーブルを立ち上げ、すでに存在する場合はそれをノックダウンするだけです。

IF EXISTS
(
    SELECT * FROM sys.tables AS T WHERE T.name = 'dbase_54462' AND T.schema_id = SCHEMA_ID('dbo')
)
BEGIN
    DROP TABLE dbo.dbase_54462;
END

CREATE TABLE
    dbo.dbase_54462
(
    CurrentFile varchar(256) NOT NULL
,   Col1 int NOT NULL
,   Col2 varchar(50) NOT NULL
,   InsertDate datetime NOT NULL DEFAULT(CURRENT_TIMESTAMP)
);

私のForeach列挙子は、*。txtのファイルマスクに基づいて入力フォルダー内のすべてを単純に調べ、サブフォルダーをトラバースします。現在のファイル名が変数@ [User :: CurrentFileName]に割り当てられています `

enter image description here

データフローは沼地の標準です。そこで派生列変換を使用すると、現在のファイル名変数がデータフローに追加されるだけなので、テーブルに記録できます。

enter image description here

分析

私は怠惰で、処理時間を記録するために特別なことをしたくなかったので、パッケージをSSISDBカタログに展開し、そこから実行しました。

このクエリは、カタログデータを調べて、パッケージの実行時間、処理されたファイル数を確認し、ファイル数の移動平均を生成します。実行10047は不良であり、分析から除外されました。

SELECT
    E.execution_id
,   DATEDIFF(s, E.start_time, E.end_time) As duration_s
,   ES.rc AS FilesProcessed
,   AVG(ES.rc / (1.0 * DATEDIFF(s, E.start_time, E.end_time))) OVER (PARTITION BY ES.rc ORDER BY E.execution_id) AS running_average
FROM
    catalog.executions As E
    INNER JOIN
    (
        SELECT
            MIN(ES.start_time) As start_time
        ,   MAX(ES.end_time) AS end_time
        ,   count(1) As rc
        ,   ES.execution_id
        FROm
            catalog.executable_statistics AS ES
        GROUP BY
            ES.execution_id
    ) AS ES 
    ON ES.execution_id = E.execution_id
WHERE
    E.package_name = 'ReadAllTheFiles.dtsx'
    AND E.execution_id <> 10047
ORDER BY 1,2

結果のデータ(無償 SQLFiddle

execution_id    duration_s  FilesProcessed  running_average
10043   15  104 6.93333333333333
10044   13  104 7.46666666666666
10045   13  104 7.64444444444444
10050   102 1004    9.84313725490196
10051   101 1004    9.89186565715395
10052   102 1004    9.87562285640328
10053   106 1004    9.77464167060435
10055   1103    10004   9.06980961015412
10056   1065    10004   9.23161842010053
10057   1033    10004   9.38255038913446
10058   957 10004   9.65028792246735
10059   945 10004   9.83747901522255

このサンプリングサイズに基づいて、ここで説明されているように、SSISでの100、1000、または10,000ファイルの処理に大きな違いはありません。

根本原因の推定

Visual Studio(BIDS/SSDT /週の名前)内からパッケージを実行しているというDTExecUI.exeに関するコメントに基づいています。かなりの色の変更とデバッグ機能を取得するために、ネイティブ実行(dtexec.exe)はデバッグプロセスでラップされます。これにより、実行時にかなりの抵抗が生じます。

設計環境を使用してパッケージを作成し、より小さなデータセットに対してそれらを実行します。大きいものは、非グラフィカルおよび非デバッガーの実行インターフェイス(VSでのShift-F5、SSISカタログへの展開とそこからの実行、またはシェルからコマンドラインインターフェイスへの実行、およびdtutil.exeの使用)を通じて最適に処理されます。

9
billinkc