ストアドプロシージャを並列で実行する


9

同じストアドプロシージャを、異なるパラメーターを使用して複数回同時に実行しようとしています。

SQL 2014を使用しています

これは、手順が完了するまでに約7時間かかるためです。実際には同じプロセスを何度も実行します。したがって、たとえば、ブランチごとに新しいデータベースとテーブルを構築する場合があります。

ストアドプロシージャを分解して、ブランチごとに実行し、各クエリを並行して実行できるようにしたいと思います。私はこれを個別のクエリウィンドウで実行することでテストしましたが、ほぼ80%速く実行されます。

誰かが私にクエリを並行して実行するためのダミーガイドを教えてもらえますか?

回答:


8

ある時点でStackOverflowこの質問に回答しましたが、その情報をDBA.SEでも確認し、修正および更新しておくと便利です。

完全に明示的に言えば、TSQLには(それ自体)他のTSQL操作を非同期で起動する機能はありませ

それはあなたがまだ多くのオプションがないことを意味するわけではありません(他の答えで言及されているそれらのいくつか):

  • SQLエージェントジョブ:複数のSQLジョブを作成し、必要なときに実行するようにスケジュールするか、を使用して「マスターコントロール」ストアドプロシージャから非同期に開始しますsp_start_job。プログラムで進行状況を監視する必要がある場合は、ジョブごとにカスタムJOB_PROGRESSテーブルを更新するようにしてください(または、Gregory A. Larsenによるこの優れた記事でxp_sqlagent_enum_jobs説明されているドキュメント化されていない関数の使用が終了しているかどうかを確認できます)。異なるパラメーターを使用して同じストアドプロシージャを実行している場合でも、並列プロセスを実行するのと同じ数の個別のジョブを作成する必要があります。
  • SSISパッケージ:単純な分岐タスクフローでSSISパッケージを作成します。SSISはこれらのタスクを個々のspidで起動し、SQLは並列で実行します。
  • カスタムアプリケーション:選択した言語(C#、Powershellなど)で提供される非同期メソッドを使用して、その言語でシンプルなカスタムアプリを記述します。各アプリケーションスレッドでSQLストアドプロシージャを呼び出します。
  • OLEオートメーション:SQLでは、sp_oacreatesp_oamethodを使用して、この記事で説明されているように、お互いにストアドプロシージャを呼び出す新しいプロセスを起動します。これもGregory A. Larsenによるものです。
  • Service Brokerこの記事の非同期実行の良い例であるService Brokerの使用を検討してください
  • CLR並列実行:CLRコマンドParallel_AddSqlを使用し、この記事の Alan KaplanによるParallel_Execute説明に従ってください(SQL2005 +のみ)。
  • スケジュールされたWindowsタスク:完全を期すためにリストされていますが、私はこのオプションのファンではありません。

私の場合、おそらく、より単純なシナリオでは複数のSQLエージェントジョブを使用し、より複雑なシナリオではSSISパッケージを使用します。

あなたの場合、200の個別のスレッドを起動しようとしない限り、スケジュールされた複数のエージェントジョブは、単純で管理しやすい選択のように聞こえます。

最後のコメント:SQLは、可能な場合はいつでも既に個々の操作を並列化しようとしています*。つまり、2つのタスクを連続して実行するのではなく、同時に実行しても、すぐに終了する保証はありません。それが実際に何か改善するかどうかを確認するために注意深くテストしてください。

8つのタスクを同時に実行するDTSパッケージを作成した開発者がいました。残念ながら、これは4 CPUサーバーのみでした:)

*デフォルト設定を想定しています。これは、サーバーの最大並列度またはアフィニティマスクを変更するか、MAXDOPクエリヒントを使用して変更できます。


2

最善の策は、同じスケジュールで3つの個別のジョブを作成して、ジョブを同時に開始することです。ジョブの実行内容に応じて、ブロッキングとデッドロックを注意深く監視する必要があります。

別のオプションは、SPを並行して呼び出すためのN個のオペレーターを含むSSISパッケージを作成することです


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つでは、ループを実行して、同じことを実行する一連のコマンドを作成していました。このスクリプトは、スクリプトブロックを使用して各コマンドを非同期で実行しますが、実際のコマンドは異なります。物事を簡単にするために、実行したいコマンドのリストを配列に入れ、配列をループしました。


1

マルチスレッドでC#アプリを使用して、Parallel.ForEachさまざまなパラメーターでspを呼び出します。3つのセクションがあります。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

ForEach -ParallelPowershell でも使用できます。

以下の例(Powershell Run Stored Procedures in Parallel in 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

これは私が職場で使用したユースケースを思い出させるので、それをどのように解決するかを説明します。

最初にすでに述べたように、SQLにはUnixの「nohup」のようなものは存在しないと思います。1つの接続= 1つのステートメントで、すべて(ロック、コミット、エラー...)

無料のETL Talendを使用してDBに接続するように構成し、ストアドプロシージャをラップする一連の並列ジョブを実行する方法を見つけます。

Iterateコンポーネントを使用してループを必要なだけ繰り返し、multi-threadsオプションを有効にしました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.