使用一个连接执行多个SQL命令是否更好,还是每次重新连接?

49

以下是我的测试代码,它似乎表明连接多次比只连接一次更好。

我做错了什么吗?

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

更新:

好的,那些认为只使用一个连接速度更快的人是对的。(尽管差异微不足道。) 这是修订后的代码和输出:

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

这些数字相差太小,无法作为明确的基准! - Cory Nelson
2
+1 那个增量非常小。尝试运行几千次,看看你得到什么结果。此外,重新排列这两个测试,因为一些启动时间无疑会影响你的结果。 - 3Dave
1
不仅时间几乎相等,而且您直接在彼此之后进行它们,这使得第二轮更快的可能性更大,因为代码已经加载并且有潜在的缓存。连接池是关键。 - Pontus Gagge
@n8wrl,@user420667:DbCommand也实现了IDisposableSqlCommand也是如此)。 - vgru
1
嗯...我会尝试重新排序和优化它们。但我认为我应该将我的命令更改为更快的命令,因为大部分时间都花在执行命令上。 - user420667
5个回答

34

默认情况下,SqlConnection会使用连接池,因此您的代码在任何情况下都不太可能实际打开多个连接。

您可以通过在连接字符串中启用或禁用池来控制SqlConnection是否使用连接池。取决于您的连接字符串所代表的数据库,语法将有所不同。

如果您使用的是MSSQLServer,则可以查看这里了解一些信息。尝试在连接字符串中设置Pooling=false并查看是否有所不同。


12

明确地,拥有一个连接更好。也许你正在使用少量的数据运行基准测试。尝试将数量增加到1,000或10,000。

另一点是,根据您的应用程序配置,您可能认为您正在使用多个连接,但.NET正在为您汇集连接,因此您基本上是在使用相同的连接。


8
由于.NET重复使用连接(“连接池”),因此连续多次创建DbConnection实例的开销不大。ADO.NET在幕后将重新使用连接。这就是为什么每次都要处理SqlConnection对象,告诉.NET它可以将其返回到池中。
但是,您可以通过使用ADO.NET批处理来提高多个插入的性能。在这种情况下,每秒可以轻松进行数千个插入。如果性能很关键,则甚至可以考虑使用SQLBulkCopy
另外,你的第一组结果相当奇怪:100个插入需要30秒?

这仅适用于插入、更新和删除操作。基本上,仅用于向数据库写入数据,而不是查询。这样正确吗? - user420667
@user420667:没错,批处理和批量复制可以加快写入操作的速度。很抱歉,在您最初的问题中没有查询文本,我假设您正在进行插入操作(部分原因是我最近必须优化大型插入操作,所以我进行了各种性能测试)。 - vgru
呵。鉴于我的sqlcommand有些故意模糊,这还算公平。谢谢。 - user420667

5

一般来说,.NET的连接池应该会使它“不重要”,因为它能够很好地为您回收连接。但是我的做法是使用一个单独的连接来执行一堆我知道将要一起进行的事务。我认为你的计时是连接池正在发挥作用并且运行中存在普通变化的指示。


3

SqlClient会对您的连接进行池化。在您第一次打开连接时,它将执行打开连接的操作。每次运行都将使用池化的连接。如果您反转顺序并首先执行“多个连接”,我希望您能看到相反的结果。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接