web-dev-qa-db-fra.com

Est-il préférable d'exécuter plusieurs commandes sql avec une seule connexion ou de se reconnecter à chaque fois?

Voici mon code de test, qui semble suggérer qu'il vaut mieux se connecter plusieurs fois au lieu de se connecter une seule fois.

Est-ce que je fais quelque chose de mal?

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);

Production:

//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

Mise à jour:

OK, donc ceux qui ont dit que ce serait plus rapide avec une connexion l'ont. (bien que la différence soit marginale, le cas échéant.) Voici le code révisé et la sortie:

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);
}

Production:

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

Mise à jour:

la différence est plus frappante si j'utilise SQLConnection.ClearAllPools() après chaque fonction:

Production:

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

Par défaut, SqlConnection utilise le regroupement de connexions. Par conséquent, votre code n'ouvre probablement pas de nombreuses connexions dans les deux cas.

Vous pouvez contrôler si SqlConnection utilisera le regroupement en activant ou désactivant le pool dans la chaîne de connexion, selon la base de données de votre chaîne de connexion, la syntaxe variera.

Voir ici pour quelques informations si vous utilisez MSSQLServer. Essayez de définir Pooling = false dans la chaîne de connexion et voyez si cela fait une différence.

33
Ben Schwehn

En définitive, il vaut mieux avoir une connexion. Peut-être que vous exécutez votre référence avec une petite quantité de données. Essayez d'augmenter le nombre à 1 000 ou 10 000.

Un autre point est que, selon la configuration de votre application, vous pourriez penser que vous exécutez avec plusieurs connexions, mais .NET regroupe les connexions pour vous, vous exécutez donc essentiellement avec les mêmes connexions.

10
Adrian Carneiro

Étant donné que .NET réutilise les connexions ("pool de connexions"), il n'y a pas beaucoup de temps supplémentaire pour créer une nouvelle instance de DbConnection plusieurs fois de suite. ADO.NET va juste réutiliser la connexion sous le capot. C'est pourquoi il est bon que vous supprimiez à chaque fois l'objet SqlConnection, indiquant à .NET qu'il peut le renvoyer dans le pool.

Vous pouvez cependant augmenter les performances de plusieurs insertions en utilisant ADO.NET batching . Dans ce cas, vous pouvez facilement avoir plusieurs milliers d'inserts par seconde. Si les performances sont critiques, vous pouvez même envisager d'utiliser SQLBulkCopy .

De plus, votre première paire de résultats est assez étrange: 30s pour 100 inserts?

7
Groo

SqlClient regroupera vos connexions. Dans votre premier cas avec un ouvert, il fera le travail d'ouverture de la connexion. Toutes les autres exécutions utiliseront la connexion groupée. Si vous annulez votre commande et effectuez d'abord "de nombreuses connexions", je m'attendrais à ce que vous voyiez le résultat inverse.

3
James Kovacs

En général, le pool de connexions de .NET ne devrait pas avoir d'importance, car il fait un excellent travail de recyclage des connexions pour vous. Mais ma pratique consiste à utiliser une seule connexion pour un tas de transactions qui, je le sais, auront lieu ensemble. Je pense que vos horaires sont une indication du pool de connexions qui fait son travail et de simples variations dans les exécutions.

3
n8wrl