Führen Sie gespeicherte Prozeduren parallel aus


9

Ich möchte versuchen, dieselbe gespeicherte Prozedur mehrmals mit unterschiedlichen Parametern gleichzeitig auszuführen.

Ich verwende SQL 2014

Der Grund dafür ist, dass der Vorgang etwa 7 Stunden dauert. Es macht tatsächlich den gleichen Prozess viele Male. So kann beispielsweise eine neue Datenbank und Tabellen für jeden Zweig erstellt werden.

Ich möchte die gespeicherte Prozedur aufteilen, damit ich sie pro Zweig ausführen und dann jede Abfrage parallel ausführen kann. Ich habe dies getestet, indem ich es in separaten Abfragefenstern ausgeführt habe, und es läuft fast 80% schneller.

Kann mir jemand einen Dummies-Leitfaden zum parallelen Ausführen von Abfragen geben?

Antworten:


8

Irgendwann habe ich diese Frage bei StackOverflow beantwortet , aber es scheint nützlich zu sein, diese Informationen auch bei DBA.SE zu überarbeiten und zu aktualisieren.

Nur um ganz ausdrücklich: TSQL tut nicht (selbst) haben die Fähigkeit , asynchron andere TSQL Operationen zu starten .

Das bedeutet nicht, dass Sie noch nicht viele Optionen haben (einige davon werden in anderen Antworten erwähnt):

  • SQL Agent-Jobs : Erstellen Sie mehrere SQL-Jobs und planen Sie sie so, dass sie zum gewünschten Zeitpunkt ausgeführt werden, oder starten Sie sie asynchron von einem "Master Control" -Speicher mit sp_start_job. Wenn Sie ihren Fortschritt programmgesteuert überwachen müssen, stellen Sie einfach sicher, dass die Jobs jeweils eine benutzerdefinierte JOB_PROGRESS-Tabelle aktualisieren (oder Sie können überprüfen, ob sie die undokumentierte Funktion, xp_sqlagent_enum_jobswie in diesem hervorragenden Artikel von Gregory A. Larsen beschrieben, bereits verwendet haben). Sie müssen so viele separate Jobs erstellen, wie parallele Prozesse ausgeführt werden sollen, auch wenn sie denselben gespeicherten Prozess mit unterschiedlichen Parametern ausführen.
  • SSIS-Paket : Erstellen Sie ein SSIS-Paket mit einem einfachen Verzweigungs-Taskflow. SSIS startet diese Aufgaben in einzelnen Spids, die SQL parallel ausführt.
  • Benutzerdefinierte Anwendung : Schreiben Sie eine einfache benutzerdefinierte Anwendung in der Sprache Ihrer Wahl (C #, Powershell usw.) mit den von dieser Sprache bereitgestellten asynchronen Methoden. Rufen Sie einen gespeicherten SQL-Prozess für jeden Anwendungsthread auf.
  • OLE-Automatisierung : Verwenden sp_oacreateund sp_oamethodstarten Sie in SQL einen neuen Prozess, der sich gegenseitig als gespeicherten Prozess aufruft, wie in diesem Artikel beschrieben , ebenfalls von Gregory A. Larsen.
  • Service Broker : Sehen Sie sich in diesem Artikel die Verwendung von Service Broker an , ein gutes Beispiel für die asynchrone Ausführung .
  • CLR Parallel Execution : Verwenden Sie die CLR-Befehle Parallel_AddSqlund Parallel_Executewie in diesem Artikel von Alan Kaplan beschrieben (nur SQL2005 +).
  • Geplante Windows-Aufgaben : Der Vollständigkeit halber aufgeführt, aber ich bin kein Fan dieser Option.

Wenn ich es wäre, würde ich wahrscheinlich mehrere SQL Agent-Jobs in einfacheren Szenarien und ein SSIS-Paket in komplexeren Szenarien verwenden.

In Ihrem Fall klingen mehrere geplante Agent-Jobs wie eine einfache und überschaubare Wahl, es sei denn, Sie versuchen, 200 separate Threads zu starten.

Ein letzter Kommentar : SQL versucht bereits, einzelne Operationen zu parallelisieren, wann immer dies möglich ist *. Dies bedeutet, dass das gleichzeitige Ausführen von zwei Aufgaben statt nacheinander keine Garantie dafür ist, dass sie früher abgeschlossen werden. Testen Sie sorgfältig, ob sich tatsächlich etwas verbessert oder nicht.

Wir hatten einen Entwickler, der ein DTS-Paket erstellt hat, um 8 Aufgaben gleichzeitig auszuführen. Leider war es nur ein 4-CPU-Server :)

* Vorausgesetzt, Standardeinstellungen. Dies kann durch Ändern des maximalen Parallelitätsgrads oder der Affinitätsmaske des Servers oder mithilfe des MAXDOP-Abfragehinweises geändert werden.


2

Am besten erstellen Sie drei separate Jobs mit demselben Zeitplan, um die Jobs gleichzeitig zu starten. Je nachdem, was die Jobs tun, sollten Sie darauf achten, das Blockieren und Deadlocking zu überwachen.

Eine weitere Option ist das Erstellen eines SSIS-Pakets mit N Anzahlen von Operatoren, um die SPs parallel aufzurufen


2

Sie könnten Powershell verwenden. Angenommen, Sie arbeiten mit SQL Server, könnten Sie Folgendes tun: (jetzt getestet und bereinigt)

#This script creates a number of connections (one per entry in $Commands) 
# to a SQL Server instance ($Server) and database ($DBName)
#Driver variables


#Set Initial collections and objects    
$Server= "(local)\sql2016cs" ; #Server to connect to
$DBName = "Test" ; #Database to connect to

$Commands = @()
$Commands += "EXEC sp_LogMe 'a'"
$Commands += "EXEC sp_LogMe 'b'"

#Loop through commands array, create script block for establishing SMO connection/query
#Start-Job for each script block
foreach ($sql in $Commands ) {

# All of that extra information after "Smo" tells it to load just v12 (for when you have multiple
#   versions of SQL installed.)  Note: V13 is 2016.
 $cmdstr =@"
`Add-Type -AssemblyName "Microsoft.SqlServer.Smo,Version=$(13).0.0.0,Culture=neutral,PublicKeyToken=89845dcd8080cc91"
`[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo")
`$SqlConn = New-Object Microsoft.SqlServer.Management.Smo.Server ("$Server")
`$SqlConn.Databases["$DBName"].ExecuteNonQuery("$sql")
"@

#Uncomment the next like to print the command string for debugging
# $cmdstr
#Execute script block in jobs to run the command asyncronously
$cmd = [ScriptBlock]::Create($cmdstr)
Start-Job -ScriptBlock $cmd
}

Hinweis: Ich habe dies von etwas Ähnlichem übernommen, das ich hier getestet habe: https://sqlstudies.com/2016/02/24/powershell-script-to-create-multiple-sql-server-connections/

In diesem habe ich eine Schleife ausgeführt, um eine Reihe von Befehlen zu erstellen, die dasselbe tun. Dieses Skript verwendet den Skriptblock, um jeden Befehl asynchron auszuführen, jedoch mit unterschiedlichen tatsächlichen Befehlen. Zur Vereinfachung habe ich die Liste der Befehle, die Sie ausführen möchten, in ein Array eingefügt und das Array durchlaufen.


1

Ich benutze eine C # -App mit Multithread Parallel.ForEach, um sp mit verschiedenen Parametern aufzurufen. Haben Sie drei Abschnitte. Init, Body, localFinally

public void NearLinkParallelGeneration(avl_range avl_pending, DateTime dt_start_process)
    {
        var parallelOptions = new ParallelOptions
        {
            MaxDegreeOfParallelism = Environment.ProcessorCount + 2
        };

        // create the partition based on the input
        var partitions = Partitioner
                            .Create(
                                fromInclusive: avl_pending.begin,
                                toExclusive: avl_pending.end,
                                rangeSize: 100
                            )
                            .GetDynamicPartitions();

        Parallel.ForEach(
            source: partitions,
            parallelOptions: parallelOptions,
            localInit: () =>
            {
                NpgsqlConnection conn = new NpgsqlConnection(strConnection);
                NpgsqlCommand cmd = new NpgsqlCommand();
                try
                {
                    conn.Open();
                    cmd.Connection = conn;
                    cmd.CommandText = "SELECT * FROM avl_db.process_near_link(@begin, @end, @start_time);";
                    cmd.CommandType = CommandType.Text;

                    NpgsqlParameter p = new NpgsqlParameter("@begin", NpgsqlDbType.Bigint);
                    cmd.Parameters.Add(p);

                    p = new NpgsqlParameter("@end", NpgsqlDbType.Bigint);
                    cmd.Parameters.Add(p);

                    p = new NpgsqlParameter("@start_time", NpgsqlDbType.Timestamp);
                    p.Value = dt_start_process;
                    cmd.Parameters.Add(p);
                }
                catch (NpgsqlException ex)
                {
                    Console.WriteLine(ex.InnerException);
                }
                catch (System.Exception ex)
                {
                    Console.WriteLine(ex.InnerException);
                }

                return new { Connection = conn, Command = cmd };
            },
            body: (source, state, local) =>
            {
                if (local.Connection.State == ConnectionState.Open)
                {
                    string strResult = String.Format("From: {0} - To: {1}", source.Item1, source.Item2);
                    Console.WriteLine(strResult);

                    try
                    {
                        local.Command.Parameters["@begin"].Value = source.Item1;
                        local.Command.Parameters["@end"].Value = source.Item2;
                        local.Command.ExecuteNonQuery();
                    }
                    catch (NpgsqlException ex)
                    {
                        Console.WriteLine(ex.InnerException);
                    }
                    catch (System.Exception ex)
                    {
                        Console.WriteLine(ex.InnerException);
                    }

                    //strResult = String.Format("DONE From: {0} - To: {1}", source.Item1, source.Item2);
                    //Console.WriteLine(strResult);

                }
                return local;
            },
            localFinally: local =>
            {
                local.Command?.Dispose();
                local.Connection?.Dispose();
            }
        );
    }

1

Sie können auch ForEach -Parallelin Powershell verwenden.

Das folgende Beispiel (entnommen aus meiner Frage: Powershell führt gespeicherte Prozeduren parallel in der Datenbank aus ) führt alle gespeicherten Prozeduren in einer Datenbank aus:

Workflow TestRunParallelExecute
{
    $ServerName = "localhost"
    $DatabaseName = "testrun"
    $Procedure_Query = "select name from sys.procedures"
    $Procedure_List = (Invoke-Sqlcmd -Server $ServerName -Database $DatabaseName -Query $Procedure_Query)

    ForEach -Parallel ($Procedure in $Procedure_List.Name)
    {
         Invoke-Sqlcmd -Server $ServerName -Database $DatabaseName -Query $Procedure 
    }
}
TestRunParallelExecute
cls

0

Da dies mich an einen Anwendungsfall erinnert, den ich bei der Arbeit hatte, werde ich sagen, wie wir ihn lösen:

Zunächst glaube ich, wie bereits gesagt, dass es in SQL kein Unix "nohup" gibt: eine Verbindung = eine Anweisung mit allem, was dazugehört (Sperren, Festschreiben, Fehler ...)

Wir finden unseren Weg mit dem kostenlosen ETL Talend, konfigurieren es für die Verbindung mit der Datenbank und führen eine Reihe von parallelen Jobs aus, die die gespeicherte Prozedur umschließen.

Wir haben die IterateKomponente und die Schleife so oft wie nötig verwendet, um die multi-threadsOption zu aktivieren .

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.