我正在编写一个C#应用程序,需要在某个时间点将大约600K条记录插入数据库。
它们是非常简单的记录:只有3个长。
我正在使用params来设置命令,然后循环遍历内存中的数据以进行插入,在每个循环中为命令参数分配值并运行command.ExecuteNonQuery()
在SqlServer上完成需要大约50秒,而在MySql上它甚至更慢,而在平面文件上插入相同的数据只需要几毫秒。
我做错了什么或者数据库太慢了?
答案 0 :(得分:6)
由于以下几个原因,您会看到写入平面文件的速度更快:
ExecuteNonQuery
不会将多个插入语句分组到批处理中,因此每个记录会产生一个完整的进程间通信周转时间。将您的插入语句分组发送。答案 1 :(得分:3)
如果您只需要插入数据并且从不读回来那么您可以编写一个noop函数并假装您将它们插入到/ dev / nul中。真正的问题是您打算如何使用上述数据?您是否需要查询,过滤,排序,引用各个记录? IE浏览器。为什么你甚至考虑一个数据库开始,如果一个平面文件看起来一样好?
使用SQL Server,您当然可以使用数据库获得更好的性能,并且至少以每秒约50-100k的速率插入。您当前的阻塞点可能是每个刀片上的lgo冲洗。您必须批量提交并确保您的日志位于快速的主轴阵列上。启动一个事务,插入足够大的记录来填充日志页面(64kb)然后提交。同样值得使用5-10个SqlCommands和连接的电池,并使用异步命令(带回调的BeginExecuteNonReader)并行启动多个插入,这样您就可以利用现在在网络往返和执行上下文准备中丢失的所有死区时间。
答案 2 :(得分:2)
因此,单行约为8毫秒,而整个文件约为8毫秒。公平?
数据库当然还有很多可能发生的事情:
我假设您在本地运行,因此无需包含网络延迟。
所以我猜想数据库速度较慢。不过,我不会想到600K的速度。
答案 3 :(得分:2)
使用SqlBulkCopy:
http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlbulkcopy.aspx
http://www.sqlteam.com/article/use-sqlbulkcopy-to-quickly-load-data-from-your-client-to-sql-server
答案 4 :(得分:1)
你在做批量插入吗?如果你已经存在,我会用它。
INSERT INTO dbo.NewTable(fields)
SELECT fields
FROM dbo.oldTable
WHERE ...
在上面的示例中,您需要确保select语句中使用的表具有适当的索引...正确地将聚簇索引分配给最相关的字段。
如果select语句很慢,请检查执行计划以找到瓶颈。
答案 5 :(得分:1)
我对MySQL无法帮助你。但是,SQL Server 2005及更高版本具有一些非常有趣的XML支持,可能会帮助您。我建议您查看Updategrams,这项功能允许您提交要插入,更新或删除的一批数据。这可能有助于您提高SQL Server的性能,因为您只需要发出一个语句而不是600,000个语句。我不确定它是否会像写入原始文件一样快,但它应该比发出单个语句快得多。
您可以在此处开始了解更新图:http://msdn.microsoft.com/en-us/library/aa258671(SQL.80).aspx
答案 6 :(得分:1)
正如亚历克斯所说:使用SqlBulkCopy,在性能方面没有什么比这更好的了。
使用起来有点棘手,示例代码可以看一下:
http://github.com/SamSaffron/So-Slow/blob/1552b1293525bfe36f6c9b522e370de626ac6f05/Importer.cs
答案 7 :(得分:1)
Ayende有一些有趣的代码来准确批量处理这些ExecuteNonQuery
情况。 Opening Up Query Batching是介绍SqlCommandSet
的介绍帖子,然后在There Be Dragons: Rhino.Commons.SqlCommandSet中发布代码。
如果您可以针对SQL2008进行优化,您还可以尝试闪亮的新表值参数。 This sqlteam article对他们来说是一个很好的介绍。
答案 8 :(得分:0)
你可能一遍又一遍地对数据库服务器运行命令,如果你构造一个包含多个插入的命令文本然后运行它会怎样?即
string commandText = "insert into x ( y, z) values ( 1, 2 );\r\n"
commandText += "insert into x ( y, z) values ( 2, 3 );"
command.Text = commandText;
command.ExecuteNonQuery();
答案 9 :(得分:0)
如果您不需要许多并发用户,请尝试使用MS-Jet,即“Microsoft Access”作为您的DBMS。 MSJet性能可以比SqlServer快约10倍。顺便说一句,在50秒内(12k / sec)插入600k记录对SqlServer来说非常快。
答案 10 :(得分:0)
我的猜测是你正在进行事务性插入:插入看起来像这样:
INSERT INTO dbo.MyTable (Field1, Field2, Field3)
VALUES (50, 100, 150)
那会起作用,但就像你发现的那样,它不会扩展。为了将大量数据快速地推送到SQL Server,有一些工具和技术可以实现它。
可能最简单的方法是使用BCP。这里有几个关于它的链接:
接下来,您需要设置SQL Server以插入尽可能多的记录。您的数据库是处于完全恢复模式还是简单恢复模式?要找到答案,请进入SQL Server Management Studio,右键单击数据库名称,然后单击“属性”。完全恢复模式将记录每个事务,但简单恢复模式将运行得更快。数据文件和日志文件是否位于不同的阵列上?每个阵列中有多少个驱动器,它是什么类型的RAID(1,5,10)?例如,如果数据和日志文件都在C驱动器上,那么性能就会很差。
接下来,您也需要设置表格。你在桌子上有约束和索引吗?你有没有其他记录,你有其他人在同一时间查询它吗?如果是这样,请考虑为没有索引或约束的数据加载构建一个空表。尽可能快地转储那里的所有数据,然后应用约束或索引,或将数据移动到其最终目的地。
答案 11 :(得分:0)
我的SQL Server 2005解决方案
StringBuilder sb = new StringBuilder();
bool bFirst = true;
foreach(Record r in myData)
{
if (bFirst)
sb.AppendLine("INSERT INTO tbl (f1, f2, f3)");
else
sb.AppendLine("UNION ALL");
bFirst = false;
sb.AppendLine("SELECT " + r.data1.ToString() + "," +
r.data2.ToString() + "," + r.data3.ToString());
}
SqlCommand cmd = new SqlCommand(sb.ToString(), conn);
cmd.ExecuteNonQuery();
想知道它会如何表现;)