web-dev-qa-db-fra.com

Exécuter des procédures stockées en parallèle

Je cherche à essayer d'exécuter la même procédure stockée plusieurs fois avec des paramètres différents mais en même temps.

J'utilise SQL 2014

La raison en est que la procédure dure environ 7 heures. Il fait en fait plusieurs fois le même processus. Ainsi, par exemple, il pourrait créer une nouvelle base de données et de nouvelles tables pour chaque branche.

Ce que je veux faire, c'est décomposer la procédure stockée afin que je puisse exécuter par branche, mais ensuite exécuter chaque requête en parallèle. J'ai testé cela en l'exécutant dans des fenêtres de requête distinctes et il s'exécute presque 80% plus rapidement.

Quelqu'un peut-il me donner un guide factice sur l'exécution de requêtes en parallèle?

9
user124569

À un moment donné, j'ai répondu à cette question sur StackOverflow , mais il semble qu'il serait utile d'avoir également ces informations sur DBA.SE, révisées et mises à jour.

Juste pour être totalement explicite: TSQL ne pas (par lui-même) a la possibilité de lancer d'autres opérations TSQL de manière asynchrone .

Cela ne signifie pas que vous n'avez pas encore beaucoup d'options (certaines d'entre elles sont mentionnées dans d'autres réponses):

  • Tâches de l'Agent SQL : créez plusieurs tâches SQL et planifiez-les pour qu'elles s'exécutent à l'heure souhaitée, ou lancez-les de manière asynchrone à partir d'un processus stocké de "contrôle maître" en utilisant sp_start_job. Si vous avez besoin de surveiller leur progression par programme, assurez-vous simplement que les travaux mettent chacun à jour une table JOB_PROGRESS personnalisée (ou vous pouvez vérifier s'ils n'ont pas encore utilisé la fonction non documentée xp_sqlagent_enum_jobs comme décrit dans cet excellent article par Gregory A. Larsen). Vous devez créer autant de tâches distinctes que vous voulez que les processus parallèles s'exécutent, même s'ils exécutent le même proc stocké avec des paramètres différents.
  • Package SSIS : créez un package SSIS avec un flux de tâches de branchement simple. SSIS lancera ces tâches dans des spids individuels, que SQL exécutera en parallèle.
  • Application personnalisée : Écrivez une application personnalisée simple dans la langue de votre choix (C #, Powershell, etc.), en utilisant les méthodes asynchrones fournies par cette langue. Appelez un proc stocké SQL sur chaque thread d'application.
  • OLE Automation : dans SQL, utilisez sp_oacreate et sp_oamethod pour lancer un nouveau processus s'appelant les uns les autres, comme décrit dans cet article , également par Gregory A. Larsen.
  • Service Broker : essayez d'utiliser Service Broker , un bon exemple d'exécution asynchrone dans cet article .
  • Exécution parallèle CLR : utilisez les commandes CLR Parallel_AddSql et Parallel_Execute comme décrit dans cet article par Alan Kaplan (SQL2005 + uniquement).
  • Tâches Windows planifiées : répertoriées pour être complètes, mais je ne suis pas fan de cette option.

Si c'était moi, j'utiliserais probablement plusieurs tâches d'agent SQL dans des scénarios plus simples et un package SSIS dans des scénarios plus complexes.

Dans votre cas, à moins que vous n'essayiez de lancer 200 threads distincts, plusieurs tâches d'agent planifiées semblent être un choix simple et gérable.

Un dernier commentaire : SQL tente déjà de paralléliser des opérations individuelles chaque fois qu'il le peut *. Cela signifie que l'exécution de 2 tâches en même temps au lieu l'une de l'autre ne garantit pas qu'elle se terminera plus tôt. Testez soigneusement pour voir si cela améliore réellement quelque chose ou non.

Nous avons eu un développeur qui a créé un package DTS pour exécuter 8 tâches en même temps. Malheureusement, ce n'était qu'un serveur à 4 CPU :)

* En supposant les paramètres par défaut. Cela peut être modifié en modifiant le degré maximal de parallélisme ou le masque d'affinité du serveur, ou en utilisant l'indicateur de requête MAXDOP.

8
BradC

Votre meilleur pari est de créer trois emplois distincts avec le même calendrier pour lancer les travaux en même temps. Selon ce que font les travaux, vous devez faire attention au contrôle du blocage et du blocage.

Une autre option consiste à créer un package SSIS avec N nombre d'opérateurs pour appeler les SP en parallèle

2
Pixelated

Vous pouvez utiliser Powershell. En supposant que vous travaillez avec SQL Server, vous pouvez faire quelque chose comme ceci: (testé et nettoyé maintenant)

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

Remarque: J'ai pris cela à partir de quelque chose de similaire que j'ai fait ici et qui est testé: https://sqlstudies.com/2016/02/24/powershell-script-to-create-multiple-sql-server-connections/

Dans celui-là, je faisais une boucle pour créer un tas de commandes faisant la même chose. Ce script utilise le bloc de script pour exécuter chaque commande de manière asynchrone mais avec différentes commandes réelles. Pour rendre les choses plus faciles, j'ai mis la liste des commandes que vous souhaitez exécuter dans un tableau et parcourir le tableau.

2
Kenneth Fisher

J'utilise une application C # avec multithread Parallel.ForEach pour appeler sp avec différents paramètres. Ont trois sections. Init, Corps, localEnfin

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

Vous pouvez aussi utiliser ForEach -Parallel dans Powershell.

L'exemple ci-dessous (tiré de ma question Powershell Run Stored Procedures in Parallel in Database ) exécutera toutes les procédures stockées dans une base de données:

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
1
user162241

Comme cela me rappelle un cas d'utilisation que j'avais au travail, je vais expliquer comment nous le résolvons:

D'abord, comme je l'ai déjà dit, je ne pense pas qu'il existe de type "Nohup" Unix dans SQL: une connexion = une instruction, avec tout ce qui va de pair (verrouiller, valider, erreur ...)

Nous trouvons notre chemin en utilisant l'ETL Talend gratuit, en le configurant pour se connecter à la base de données et en exécutant un tas de travaux parallèles encapsulant la procédure stockée.

Nous avons utilisé le composant Iterate et la boucle autant de fois que nécessaire, ce qui a permis à multi-threads option.

0
Blag