Esegui le procedure memorizzate in parallelo


9

Sto cercando di provare a eseguire la stessa procedura memorizzata più volte con parametri diversi ma allo stesso tempo.

Sto usando SQL 2014

La ragione di ciò è che il completamento della procedura richiede circa 7 ore. In realtà fa lo stesso processo molte volte. Quindi, ad esempio, potrebbe creare un nuovo database e tabelle per ciascun ramo.

Quello che voglio fare è abbattere la procedura memorizzata in modo da poter eseguire per ramo ma quindi eseguire ogni query in parallelo. Ho eseguito questo test eseguendolo in finestre di query separate ed è più veloce dell'80%.

Qualcuno può darmi una guida dei manichini per eseguire query in parallelo?

Risposte:


8

Ad un certo punto ho risposto a questa domanda su StackOverflow , ma sembra che sarebbe utile avere anche quelle informazioni su DBA.SE, riviste e aggiornate.

Giusto per essere totalmente esplicito: TSQL non ha (da solo) la possibilità di avviare altre operazioni TSQL in modo asincrono .

Ciò non significa che non hai ancora molte opzioni (alcune citate in altre risposte):

  • Processi SQL Agent : creare più processi SQL e pianificarli per l'esecuzione all'ora desiderata oppure avviarli in modo asincrono da un proc memorizzato "controllo master" mediante sp_start_job. Se devi monitorare i loro progressi a livello di codice, assicurati solo che i lavori aggiornino ciascuno una tabella JOB_PROGRESS personalizzata (oppure puoi verificare se hanno finito di usare la funzione non documentata xp_sqlagent_enum_jobscome descritto in questo eccellente articolo di Gregory A. Larsen). È necessario creare tutti i processi separati desiderati per l'esecuzione di processi paralleli, anche se eseguono lo stesso proc memorizzato con parametri diversi.
  • Pacchetto SSIS : crea un pacchetto SSIS con un semplice flusso di attività di ramificazione. SSIS avvierà tali attività in singoli spid, che SQL eseguirà in parallelo.
  • Applicazione personalizzata : scrivi una semplice app personalizzata nella lingua che preferisci (C #, Powershell, ecc.), Usando i metodi asincroni forniti da quella lingua. Chiama un proc memorizzato SQL su ogni thread dell'applicazione.
  • Automazione OLE : in SQL, utilizzare sp_oacreatee sp_oamethodavviare un nuovo processo chiamando l'altro proc memorizzato come descritto in questo articolo , anche da Gregory A. Larsen.
  • Service Broker : esaminare l'utilizzo di Service Broker , un buon esempio di esecuzione asincrona in questo articolo .
  • Esecuzione parallela CLR : utilizzare i comandi CLR Parallel_AddSqle Parallel_Executecome descritto in questo articolo di Alan Kaplan (solo SQL2005 +).
  • Attività di Windows pianificate : elencate per completezza, ma non sono un fan di questa opzione.

Se fossi in me, probabilmente utilizzerei più lavori SQL Agent in scenari più semplici e un pacchetto SSIS in scenari più complessi.

Nel tuo caso, a meno che tu non stia tentando di avviare 200 thread separati, più processi Agent programmati sembrano una scelta semplice e gestibile.

Un commento finale : SQL tenta già di parallelizzare le singole operazioni ogni volta che può *. Ciò significa che eseguire 2 attività contemporaneamente invece che una dopo l'altra non garantisce che finirà prima. Prova attentamente per vedere se migliora effettivamente qualcosa o no.

Avevamo uno sviluppatore che ha creato un pacchetto DTS per eseguire 8 attività contemporaneamente. Sfortunatamente, era solo un server a 4 CPU :)

* Supponendo le impostazioni predefinite. Questo può essere modificato alterando il grado massimo di parallelismo o maschera di affinità del server o utilizzando il suggerimento per la query MAXDOP.


2

La tua scommessa migliore è creare tre lavori separati con lo stesso programma per dare il via ai lavori contemporaneamente. A seconda di cosa stanno facendo i lavori, dovresti fare attenzione a monitorare il blocco e il deadlock.

Un'altra opzione è creare un pacchetto SSIS con N numero di operatori per chiamare gli SP in parallelo


2

Potresti usare Powershell. Supponendo che tu stia lavorando con SQL Server potresti fare qualcosa del genere: (testato e ripulito ora)

#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
}

Nota: ho preso questo da qualcosa di simile che ho fatto qui che è stato testato: https://sqlstudies.com/2016/02/24/powershell-script-to-create-multiple-sql-server-connections/

In quello stavo eseguendo un ciclo per creare un gruppo di comandi facendo la stessa cosa. Questo script utilizza il blocco di script per eseguire ogni comando in modo asincrono ma con comandi effettivi diversi. Per semplificare le cose, ho inserito l'elenco dei comandi che si desidera eseguire in un array e scorrere in un array.


1

Uso un'app C # con multithread Parallel.ForEachper chiamare sp con parametri diversi. Hanno tre sezioni. 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

Puoi anche usarlo ForEach -Parallelin Powershell.

L'esempio seguente (tratto dalla mia domanda Powershell Run Stored Procedures in Parallel in Database ) eseguirà tutte le procedure memorizzate in un database:

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

Poiché questo mi ricorda un caso d'uso che ho avuto al lavoro, metterò come lo risolviamo:

Innanzitutto, come già detto, non credo che in SQL esista alcun "nohup" simile a Unix: una connessione = un'istruzione, con tutto ciò che segue (blocco, commit, errore ...)

Troviamo la nostra strada usando l'ETL Talend gratuito, configurandolo per la connessione al DB ed eseguendo un gruppo di lavori paralleli che avvolgono la procedura memorizzata.

Abbiamo usato il Iteratecomponente e il loop tutte le volte che abbiamo bisogno, abilitando l' multi-threadsopzione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.