并行运行存储过程


9

我正在尝试尝试同时使用不同的参数多次运行相同的存储过程。

我正在使用SQL 2014

这样做的原因是该过程大约需要7个小时才能完成。实际上,它多次执行相同的过程。因此,例如,它可能会为每个分支建立一个新的数据库和表。

我想要做的是分解存储过程,这样我可以在每个分支中运行,但随后并行运行每个查询。我已经通过在单独的查询窗口中运行它进行了测试,并且运行速度快了将近80%。

谁能给我一个虚拟的指南,以并行运行查询?

Answers:


8

有一次,我在StackOverflow上回答了这个问题,但是在DBA.SE上进行修改和更新似乎也很有用。

只是为了完全明确:TSQL确实(本身)具有异步启动其他TSQL操作的能力

这并不意味着您仍然没有很多选择(其他答案中提到了其中一些):

  • SQL代理作业:创建多个SQL作业,然后安排它们在所需的时间运行,或者使用来从“主控件”存储的proc中异步启动它们sp_start_job。如果您需要以编程方式监视其进度,只需确保每个作业都更新一个自定义的JOB_PROGRESS表即可(或者您可以xp_sqlagent_enum_jobs按照Gregory A. Larsen的这篇出色文章中的说明,检查它们是否已经使用了未记录的功能)。您必须根据要运行的并行进程创建尽可能多的单独作业,即使它们正在运行具有不同参数的相同存储过程。
  • SSIS包:使用简单的分支任务流创建SSIS包。SSIS将在单个spid中启动这些任务,SQL将并行执行这些任务。
  • 自定义应用程序:使用该语言提供的异步方法,用您选择的语言(C#,Powershell等)编写一个简单的自定义应用程序。在每个应用程序线程上调用一个SQL存储的proc。
  • OLE自动化:在SQL中,使用sp_oacreatesp_oamethod启动一个新进程,以互相调用存储的proc,如本文所述,Gregory A. Larsen也是如此。
  • Service Broker:研究使用Service Broker这是本文异步执行的一个很好的例子
  • CLR并行执行:使用CLR命令Parallel_AddSqlParallel_Execute如Alan Kaplan 在本文中所述(仅适用于SQL2005 +)。
  • 预定的Windows任务:出于完整性考虑列出,但我不喜欢此选项。

如果是我,则可能会在较简单的方案中使用多个SQL Agent Job,而在较复杂的方案中使用SSIS软件包。

就您而言,除非您尝试启动200个单独的线程,否则多个计划的Agent作业听起来像是一个简单且易于管理的选择。

最后一条评论:SQL已经尝试在可能的情况下并行化各个操作*。这意味着不能同时运行两个任务,而不能同时运行两个任务,这不能保证它会更快地完成。仔细测试以查看它是否真的可以改善任何东西。

我们有一个开发人员创建了一个DTS包,以同时运行8个任务。不幸的是,它只是一个4-CPU服务器:)

*假设为默认设置。可以通过更改服务器的“最大并行度”或“相似性掩码”或使用MAXDOP查询提示来进行修改。


2

最好的选择是按照相同的时间表创建三个单独的作业,以同时启动这些作业。根据作业的执行情况,您应该小心监视阻塞和死锁。

另一种选择是创建一个具有N个操作员的SSIS包,以并行调用SP。


2

您可以使用Powershell。假设您正在使用SQL Server,则可以执行以下操作:(现在经过测试和清理)

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

注意:我从这里经过测试的类似内容中获取了此信息:https : //sqlstudies.com/2016/02/24/powershell-script-to-create-multiple-sql-server-connections/

在那个我正在运行一个循环,以创建一堆执行相同操作的命令。该脚本使用脚本块异步运行每个命令,但使用不同的实际命令。为了简化操作,我将要运行的命令列表放入数组中并遍历该数组。


1

我使用带有多线程的C#应用​​程序Parallel.ForEach来调用具有不同参数的sp。分为三个部分。Init,Body,local最后

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

您也可以ForEach -Parallel在Powershell中使用。

下面的示例(摘自我的问题Powershell在数据库中并行运行存储过程 )将在数据库中运行所有存储过程:

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

这让我想起了我曾经在工作的一个用例,我将介绍如何解决它:

首先,正如我已经说过的那样,我认为SQL中不存在任何类似Unix的“ nohup”:一个connexion =一个语句,以及所有发生的事情(锁定,提交,错误...)。

我们找到了使用免费ETL Talend的方法,将其配置为连接到数据库,然后运行一堆并行过程来包装存储过程。

我们根据需要使用了Iterate组件并循环了许多次,从而启用了该multi-threads选项。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.