web-dev-qa-db-ja.com

1つの接続で多くのSQLコマンドを実行するか、毎回再接続する方が良いですか?

テストコードは次のとおりです。1回だけ接続するのではなく、複数回接続することをお勧めします。

私は何か間違っていますか?

_int numIts = 100;
Stopwatch sw = new Stopwatch();
sw.Start();
using (SqlConnection connection = new SqlConnection(connectionParameters))
{   
            connection.Open();
    for(int i = 0; i < numIts; i++)
    {
        SqlCommand command = new SqlCommand(sqlCommandName, connection);
                command.CommandType = CommandType.StoredProcedure;
                command.Parameters.AddWithValue(par1Name, par1Val);
                command.Parameters.AddWithValue(par2Name, par2Val);
        using(SqlDataReader reader = command.ExecuteReader())
        {
        }
    }
}
sw.Stop();
TimeSpan durationOfOneConnectionManyCommands = sw.Elapsed;
Console.WriteLine(durationOfOneConnectionManyCommands);

sw.Reset();

sw.Start();
for(int i = 0; i < numIts; i++)
{
    using (SqlConnection connection = new SqlConnection(connectionParameters))
    {   
                connection.Open();
        SqlCommand command = new SqlCommand(sqlCommandName, connection);
                command.CommandType = CommandType.StoredProcedure;
                command.Parameters.AddWithValue(par1Name, par1Val);
                command.Parameters.AddWithValue(par2Name, par2Val);
        using(SqlDataReader reader = command.ExecuteReader())
        {
        }
    }                               
}
sw.Stop();
TimeSpan durationOfManyConnections = sw.Elapsed;
Console.WriteLine(durationOfManyConnections);
_

出力:

_//output:
//00:00:24.3898218   // only one connection established
//00:00:23.4585797   // many connections established.
//
//output after varying parameters (expected much shorter):
//00:00:03.8995448
//00:00:03.4539567
_

更新:

わかりました。したがって、1つの接続で高速になると言った人はそれを持っています。 (違いはわずかですが、もしあれば)。修正されたコードと出力は次のとおりです。

_public void TimingTest()
{
    numIts = 1000;
    commandTxt = "select " + colNames + " from " + tableName;

    OneConnection();
    ManyConnections();
    OneConnection();
}
private void ManyConnections()
{
    Stopwatch sw = new Stopwatch();
    sw.Start();
    for (int i = 0; i < numIts; i++)
    {
        using (SqlConnection connection = new SqlConnection(connectionParameters))
        {
            connection.Open();
            using (SqlCommand command = connection.CreateCommand())
            {
                command.CommandText = commandTxt;

                using (SqlDataReader reader = command.ExecuteReader())
                {
                }
            }
        }
    }
    sw.Stop();
    TimeSpan durationOfManyConnections = sw.Elapsed;
    Console.WriteLine("many connections: " + durationOfManyConnections);
}
private void OneConnection()
{
    Stopwatch sw = new Stopwatch();
    sw.Start();
    using (SqlConnection connection = new SqlConnection(connectionParameters))
    {
        connection.Open();
        for (int i = 0; i < numIts; i++)
        {
            using (SqlCommand command = connection.CreateCommand())
            {
                command.CommandText = commandTxt;
                using (SqlDataReader reader = command.ExecuteReader())
                {
                }
            }
        }
    }
    sw.Stop();
    TimeSpan durationOfOneConnectionManyCommands = sw.Elapsed;
    Console.WriteLine("one connection: " + durationOfOneConnectionManyCommands);
}
_

出力:

_one connection: 00:00:08.0410024
many connections: 00:00:08.7278090
one connection: 00:00:08.6368853

one connection: 00:00:10.7965324
many connections: 00:00:10.8674326
one connection: 00:00:08.6346272
_

更新:

各関数の後にSQLConnection.ClearAllPools()を使用すると、違いがより顕著になります。

出力:

_one connection: 00:00:09.8544728
many connections: 00:00:11.4967753
one connection: 00:00:09.7775865
_
42
user420667

デフォルトでは、SqlConnectionは接続プーリングを使用します。したがって、どちらの場合でも、コードが実際に多くの接続を開くことはほとんどありません。

接続文字列のDBに応じて、接続文字列のプールを有効または無効にすることで、SqlConnectionがプーリングを使用するかどうかを制御できます。構文は異なります。

MSSQLServerを使用する場合の情報については、 here を参照してください。接続文字列でPooling = falseを設定して、違いが生じるかどうかを確認してください。

33
Ben Schwehn

間違いなく、接続は1つにする方が良いでしょう。たぶん、少量のデータでベンチマークを実行しています。数を1,000または10,000に増やしてみてください。

もう1つのポイントは、アプリの構成によっては、複数の接続で実行していると思われるかもしれませんが、.NETは接続をプールしているため、基本的に同じ接続で実行しているということです。

10
Adrian Carneiro

.NETは接続を再利用するため(「接続プーリング」)、DbConnectionの新しいインスタンスを数回連続して作成する際のオーバーヘッドはあまりありません。 ADO.NETは、内部で接続を再利用するだけです。そのため、毎回SqlConnectionオブジェクトを破棄し、.NETにそれをプールに返すことができると伝えるのが良いのです。

ただし、 ADO.NETバッチ処理 を使用して、複数の挿入のパフォーマンスを向上させることができます。その場合、1秒間に数千の挿入を簡単に行うことができます。パフォーマンスが重要な場合は、 SQLBulkCopy の使用を検討することもできます。

また、最初の結果のペアは非常に奇妙です。100回の挿入で30秒ですか?

7
Groo

SqlClientは接続をプールします。最初のケースでは、1つを開くと、接続を開く作業を行います。他のすべての実行では、プールされた接続が使用されます。順序を逆にして、最初に「多くの接続」を行うと、逆の結果が表示されることが予想されます。

3
James Kovacs

一般に、.NETの接続プーリングは、接続をリサイクルするという素晴らしい仕事をするため、「問題ではない」はずです。しかし、私の習慣は、一緒に行われることがわかっている一連のトランザクションに単一の接続を使用することです。あなたのタイミングは、接続プールがその仕事をしていることの兆候であり、実行の単なる変化であると思います。

3
n8wrl