我正在处理实时数字波形的电子设备(每个设备每秒生成大约1000个512字节的数组-我们有12台设备)。我用C#为这些设备编写了一个客户端,大部分工作都正常,没有性能问题。
然而,应用程序的要求之一是存档,Microsoft SQL Server 2010被规定为存储机制(超出我的控制范围)。数据库布局非常简单:每天每个设备都有一个表格(例如,“Archive_Dev02_20131015”)。每个表格都有一个“Id”列、一个“时间戳”列、一个“Data”列(varbinary),还有20个整数列和一些元数据。在“Id”和“timestamp”上有一个聚集主键,另外一个独立的索引在“timestamp”上。我的天真方法是在客户端应用程序中将所有数据排队,然后使用SqlCommand每5秒钟将所有数据插入到数据库中。
基本机制如下所示:
简而言之:一批包含一个循环的交易,生成插入语句并执行。但这种方法非常慢。在我的电脑上(i5-2400 @ 3.1GHz、8GB RAM、使用.NET 4.0和SQL Server 2008、2个内部硬盘镜像),保存来自两个设备的数据需要大约2.5秒,因此每5秒保存12个设备的数据是不可能的。相比之下,我编写了一个小的SQL脚本(实际上是从C#与SQL服务器分析器一起运行的代码中提取出来),直接在服务器上执行相同的操作(仍在我的电脑上运行)。
然而,应用程序的要求之一是存档,Microsoft SQL Server 2010被规定为存储机制(超出我的控制范围)。数据库布局非常简单:每天每个设备都有一个表格(例如,“Archive_Dev02_20131015”)。每个表格都有一个“Id”列、一个“时间戳”列、一个“Data”列(varbinary),还有20个整数列和一些元数据。在“Id”和“timestamp”上有一个聚集主键,另外一个独立的索引在“timestamp”上。我的天真方法是在客户端应用程序中将所有数据排队,然后使用SqlCommand每5秒钟将所有数据插入到数据库中。
基本机制如下所示:
using (SqlTransaction transaction = connection.BeginTransaction()
{
//Beginning of the insert sql statement...
string sql = "USE [DatabaseName]\r\n" +
"INSERT INTO [dbo].[Archive_Dev02_20131015]\r\n" +
"(\r\n" +
" [Timestamp], \r\n" +
" [Data], \r\n" +
" [IntField1], \r\n" +
" [...], \r\n" +
") \r\n" +
"VALUES \r\n" +
"(\r\n" +
" @timestamp, \r\n" +
" @data, \r\n" +
" @int1, \r\n" +
" @..., \r\n" +
")";
using (SqlCommand cmd = new SqlCommand(sql))
{
cmd.Connection = connection;
cmd.Transaction = transaction;
cmd.Parameters.Add("@timestamp", System.Data.SqlDbType.DateTime);
cmd.Parameters.Add("@data", System.Data.SqlDbType.Binary);
cmd.Parameters.Add("@int1", System.Data.SqlDbType.Int);
foreach (var sample in samples)
{
cmd.Parameters[0].Value = amples.ReceiveDate;
cmd.Parameters[1].Value = samples.Data; //Data is a byte array
cmd.Parameters[1].Size = samples.Data.Length;
cmd.Parameters[2].Value = sample.IntValue1;
...
int affected = cmd.ExecuteNonQuery();
if (affected != 1)
{
throw new Exception("Could not insert sample into the database!");
}
}
}
}
transaction.Commit();
}
简而言之:一批包含一个循环的交易,生成插入语句并执行。但这种方法非常慢。在我的电脑上(i5-2400 @ 3.1GHz、8GB RAM、使用.NET 4.0和SQL Server 2008、2个内部硬盘镜像),保存来自两个设备的数据需要大约2.5秒,因此每5秒保存12个设备的数据是不可能的。相比之下,我编写了一个小的SQL脚本(实际上是从C#与SQL服务器分析器一起运行的代码中提取出来),直接在服务器上执行相同的操作(仍在我的电脑上运行)。
set statistics io on
go
begin transaction
go
declare @i int = 0;
while @i < 24500 begin
SET @i = @i + 1
exec sp_executesql N'USE [DatabaseName]
INSERT INTO [dbo].[Archive_Dev02_20131015]
(
[Timestamp],
[Data],
[int1],
...
[int20]
)
VALUES
(
@timestamp,
@data,
@compressed,
@int1,
...
@int20,
)',N'@timestamp datetime,@data binary(118),@int1 int,...,@int20 int,',
@timestamp='2013-10-14 14:31:12.023',
@data=0xECBD07601C499625262F6DCA7B7F4AF54AD7E074A10880601324D8904010ECC188CDE692EC1D69472329AB2A81CA6556655D661640CCED9DBCF7DE7BEFBDF7DE7BEFBDF7BA3B9D4E27F7DFFF3F5C6664016CF6CE4ADAC99E2180AAC81F3F7E7C1F3F22FEEF5FE347FFFDBFF5BF1FC6F3FF040000FFFF,
@int=0,
...
@int20=0
end
commit transaction
这次我使用了24500次迭代来模拟12个设备,实际上做的是相同的事情(我的想法,但我可能错了 ;))。 这个查询大约需要2秒钟。 如果我使用与C#版本相同数量的迭代,查询只需要不到1秒钟的时间。
所以我的第一个问题是:为什么在 SQL Server 上运行速度比在 C# 上要快得多?这是否与连接(本地 TCP)有关?
更让我困惑的是,这段代码在生产服务器上运行的速度要慢两倍(IBM BladeCenter,32GB RAM,光纤连接到 SAN,...文件系统操作非常快)。 我尝试查看SQL活动监视器,写入性能从未超过2MB / sec,但这也可能是正常现象。 我对SQL Server完全是新手(事实上与称职的 DBA 相差甚远)。
有什么想法可以让C#代码更具性能?
USE [DatabaseName]
吗? - Steve