性能上的数据库性能差异:很多小操作与一些较大的操作
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分53秒
- 全文:4分31秒
服务器 1(应用程序、数据库通过网络):
- 寻键:30m49s (!!!!)
- 全文:9分19秒
数据库服务器:
- 寻键:7m07s
- 全文:8分45秒
差异似乎与网络有关,但服务器都是位于同一硬件上的虚拟机。有谁知道,是什么导致应用服务器性能不佳?
很多小手术明显不如几个大手术。您 运行 针对 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 服务器连接配置或优化查询。
我目前正在调试一个应用程序的一些性能问题,该应用程序使用 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分53秒
- 全文:4分31秒
服务器 1(应用程序、数据库通过网络):
- 寻键:30m49s (!!!!)
- 全文:9分19秒
数据库服务器:
- 寻键:7m07s
- 全文:8分45秒
差异似乎与网络有关,但服务器都是位于同一硬件上的虚拟机。有谁知道,是什么导致应用服务器性能不佳?
很多小手术明显不如几个大手术。您 运行 针对 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 服务器连接配置或优化查询。