性能上的数据库性能差异:很多小操作与一些较大的操作

Database performance difference in performance: a lot of small operations vs some larger operations

我目前正在调试一个应用程序的一些性能问题,该应用程序使用 SQL Server 作为基础数据库,Entity Framework Core 作为 ORM。

我编写了一个小型控制台应用程序来模拟一些工作负载:

SqlConnection sqlConnection1;      

sqlConnection1 = new SqlConnection("Data Source=.\SQLEXPRESS;Initial Catalog=Database;Integrated Security=True;MultipleActiveResultSets=true");

SqlCommand cmd = new SqlCommand();
SqlDataReader reader;

Console.WriteLine("==== Table1 (1000000) key-lookup ====");

var sw1 = new Stopwatch();
sw1.Start();

for (int i = 0; i < 1000000; i++)
{
     cmd.CommandText = "SELECT Value FROM table1 WHERE Id='" + i + "'";
     cmd.CommandType = CommandType.Text;
     cmd.Connection = sqlConnection1;

     sqlConnection1.Open();

     reader = cmd.ExecuteReader();

     var result = reader.Read();

     if (result)
     {
         var test = reader.GetString(0);
     }

     sqlConnection1.Close();
}

sw1.Stop();

Console.WriteLine("Finished: " + sw1.Elapsed);
Console.WriteLine("==== Table2 (1000) full-text-scan ====");

var sw2 = new Stopwatch();
sw2.Start();

for (int i = 0; i < 1000; i++)
{
    cmd.CommandText = "SELECT Name FROM table2 WHERE Name LIKE '%" + i + "%'";
    cmd.CommandType = CommandType.Text;
    cmd.Connection = sqlConnection1;

    sqlConnection1.Open();

    reader = cmd.ExecuteReader();

    var result = reader.Read();

    if (result)
    {
        var test = reader.GetString(0);
    }

    sqlConnection1.Close();
}

sw2.Stop();

Console.WriteLine("Finished: " + sw2.Elapsed);
Console.ReadKey();

为了模拟 Entity Framework 我为每个查询打开和关闭了连接。

应用程序安装在 server1 上,数据库位于数据库服务器上,当我 运行 这个应用程序时,我得到以下结果:

我的机器(应用程序 + SQL 服务器在同一系统上):

服务器 1(应用程序、数据库通过网络):

数据库服务器:

差异似乎与网络有关,但服务器都是位于同一硬件上的虚拟机。有谁知道,是什么导致应用服务器性能不佳?

很多小手术明显不如几个大手术。您 运行 针对 SQL 服务器的任何查询都会产生一些与网络相关的开销。

我不熟悉 VM 管理,但无论它们是否位于同一台物理服务器上,都会有与 TCP-IP 通信相关的开销。快速查询将承受巨大的通信开销。

一个有趣的测试是分批划分您的查询。一种快速方法(仅限语句构造):

const int batchSize = 100;
for (int j = 0; j < batchSize; j ++)
{
    string inStr = string.Join("AND ", $"Name LIKE '%{i+j}%'");
    cmd.CommandText = $"SELECT Name FROM table2 WHERE 1 = 1 {inStr}";
}

此外,了解是否使用连接池很重要。如果你不使用它,关闭连接在你的场景中真的很昂贵(打开连接,运行 一个查询,关闭它)。 我注意到你的连接字符串,看起来你正在使用连接池(默认激活)。

我不喜欢这节:

To simulate Entity Framework I have opened and closed the connection for each query.

创建和关闭连接是相当 "expensive" 的过程,因此有一篇 Connection pool software design pattern. See Using Connection Pooling with SQL Server 文章了解详细信息。

另外,不建议在同一台主机上安装负载生成器(在您的情况下 - command-line 应用程序)和 SQL 服务器实例,以避免在使用操作系统资源时相互干扰和冲突。

我建议使用第 3 方工具,即 Apache JMeter which provides JDBC Request sampler 来执行负载,这样您将更清楚地了解查询响应时间,并且能够将增加的响应时间与增加的响应时间相关联加载。当您到达响应时间将超过可接受阈值(又名 "bottleneck")的地步时,您将能够看到根本原因并通过添加更多资源或修改您的 SQL 服务器连接配置或优化查询。