Warum zählt SSIS nur langsam viele Dateien in einem Verzeichnis auf und importiert sie?


7

Ich habe ein furchtbar langsames SSIS-Paket. Es ist ziemlich schnell mit einer Datei und ziemlich schnell mit 100 Dateien oder weniger. (Ungefähr eine Sekunde pro Datei)

Wenn mein Verzeichnis jedoch Tausende (sehr kleine) Dateien enthält, zieht sich der Prozess furchtbar langsam hin. Ich bevorzuge es, diesen Prozess nur außerhalb der Geschäftszeiten auszuführen, aber wenn ich bis dahin warte, liegt die Anzahl der zu importierenden Flatfiles bei Tausenden.

Das Paket ist sehr einfach:

  • Die äußere Schleife ist für alle (Dateiaufzählung, Dateipfad in Variable lesen)
  • Im Inneren einfach importieren, ohne die Daten zu transformieren

Das ist es.

Die Leistung mit Tausenden von Dateien beträgt 15 Sekunden oder mehr pro Datei. Die Benutzeroberfläche (Status) zeichnet / scrollt so langsam, dass ich nicht einmal sehen kann, wo sie sich befindet - die gestempelte Zeit ist bei einer Ausführung, die vor 18 Stunden gestartet wurde, mehr als 15 Stunden alt.

Version: MSSQL 2012


2
Haben Sie etwas mit der TransactionOption (Standard ist Unterstützt) im Paket gemacht?
Billinkc

1
Wir hatten eine ähnliche Frage zu SO SSIS. Die Verarbeitung einer großen Menge von
Flatfiles

2
Es kann interessant sein, den Datenfluss zu deaktivieren und zu sehen, wie lange es dauert, bis der foreach-Enumerator alle diese Dateien durchläuft. Ich würde erwarten, dass es bissig ist
billinkc

1
Vielleicht ein bereits erprobter Vorschlag, aber verschieben Sie die Dateien in nummerierten Ordnern (jeweils mit 100 Dateien) und verarbeiten Sie sie nacheinander.
Marian

1
Gibt es Belege für die Annahme, dass die Anzahl der Dateien das Problem ist? Was macht dieser Prozess mit den Dateien? Fügen Sie beispielsweise Daten in einen ständig wachsenden Heap ein oder fragmentieren Sie die Clustertabelle ständig?
Mark Storey-Smith

Antworten:


9

Ich denke, Sie stoßen auf eine Einschränkung der Benutzeroberfläche / des Debuggers.

Ich habe zwei Pakete erstellt: MakeAllTheFiles und ReadAllTheFiles

MakeAllTheFiles akzeptiert als Eingabe die Anzahl der zu erstellenden Dateien. Es wird eine Pseudozufallsfunktion verwenden, um die Daten auf eine Anzahl (7) von Unterordnern zu verteilen.

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

Das allgemeine Erscheinungsbild der Verpackung ist somit

Lesen Sie alle Dateien!

Ich habe zwei Verbindungsmanager definiert: Einer bezieht sich auf meine Datenbank und der andere auf eine flache Datei mit einem Ausdruck in der ConnectionString-Eigenschaft, sodass meine Variable verwendet wird @[User::CurrentFileName]

Variablen, ich mag viele Variablen, also gibt es viele.

Geben Sie hier die Bildbeschreibung ein

Meine Execute SQL-Aufgabe stellt einfach eine Tabelle auf, in die ich schreiben kann, und schlägt sie herunter, wenn sie bereits vorhanden ist.

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)
);

Mein Foreach Enumerator betrachtet einfach alles in meinem Eingabeordner basierend auf der Dateimaske von * .txt und durchläuft Unterordner. Der aktuelle Dateiname wird meiner Variablen @ [User :: CurrentFileName] `zugewiesen

Geben Sie hier die Bildbeschreibung ein

Der Datenfluss ist Moorstandard. Die dort abgeleitete Spaltentransformation fügt einfach die Variable Aktueller Dateiname zum Datenfluss hinzu, damit ich sie in meiner Tabelle aufzeichnen kann.

Geben Sie hier die Bildbeschreibung ein

Analyse

Ich bin faul und wollte nichts Besonderes tun, um die Verarbeitungszeiten aufzuzeichnen. Deshalb habe ich meine Pakete im SSISDB-Katalog bereitgestellt und von dort aus ausgeführt.

Diese Abfrage überprüft die Katalogdaten, um herauszufinden, wie lange das Paket ausgeführt wurde, wie viele Dateien es verarbeitet hat, und generiert dann einen laufenden Durchschnitt für die Anzahl der Dateien. Lauf 10047 war schlecht und wurde von der Analyse ausgeschlossen.

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

Die resultierenden Daten (kostenlose 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

Aufgrund dieser Stichprobengröße sehe ich keinen nennenswerten Unterschied zwischen der Verarbeitung von 100, 1000 oder 10.000 Dateien mit SSIS, wie hier beschrieben.

Grundursachenannahme

Basierend auf dem Kommentar dazu DTExecUI.exeheißt es, dass Sie das Paket in Visual Studio ausführen (BIDS / SSDT / Name der Woche). Um die hübschen Farbänderungen und Debugging-Funktionen zu erhalten, wird die native Ausführung (dtexec.exe) in den Debugging-Prozess eingebunden. Dies führt zu einer spürbaren Beeinträchtigung der Ausführung.

Verwenden Sie die Entwurfsumgebung, um Ihre Pakete zu erstellen und für kleinere Datensätze auszuführen. Größere werden am besten über die nicht grafischen und nicht debuggerischen Ausführungsschnittstellen verarbeitet (Shift-F5 in VS, Bereitstellung im SSIS-Katalog und Ausführung von dort oder Shell zur Befehlszeilenschnittstelle und Verwendung von dtutil.exe).


Ich fordere Ihr Zitat heraus: "Ich bin faul." Mir scheint, du bist es nicht. Gute Antwort. Ich habe diese Suche abgebrochen, um das SSIS durch eine gespeicherte CLR-Prozedur zu ersetzen, die schnell raucht (wie ich vermute, würde dies passieren, wenn ich sie gemäß Ihrem Vorschlag in Ihrem abschließenden Absatz bereitstellen würde). Vielen Dank für eine so artikulierte Antwort!
Chris Adragna
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.