繁体   English   中英

600K记录的数据库或平面文件?

[英]Database or flat file for 600K records?

我正在编写一个C#应用程序,需要在某个时间点将大约600K条记录插入数据库。

它们是非常简单的记录:只有3个长。

我正在使用params来设置命令,然后循环遍历内存中的数据以进行插入,在每个循环中将值分配给命令参数并运行command.ExecuteNonQuery()

在SqlServer上完成大约需要50秒,而在MySql上它甚至更慢,而在平面文件上插入相同的数据只需要几毫秒。

我做错了什么或数据库太慢了?

由于以下几个原因,您将看到写入平面文件的速度更快:

  • ExecuteNonQuery不会将多个插入语句分组到批处理中,因此每个记录都会产生一个完整的进程间通信周转时间。 以组的形式发送插入语句。
  • 您拥有的数据已经是平面文件的形状,因此您可以通过一次写入或一些带缓冲的写入来解除所有数据。
  • 数据库操作倾向于使用n log n时间的树,而简单的数组形状构造将花费线性时间。 另一方面,如果您要合并到已排序的平面文件中,则需要一段时间。

如果您只需要插入数据并且从不读回来那么您可以编写一个noop函数并假装您将它们插入到/ dev / nul中。 真正的问题是你打算如何消费这些数据 您是否需要查询,过滤,排序,引用各个记录? IE浏览器。 为什么你甚至考虑一个数据库开始,如果一个平面文件看起来一样好?

使用SQL Server,您当然可以使用数据库获得更好的性能,并且至少以每秒约50-100k的速率插入。 您当前的阻塞点可能是每个刀片上的lgo冲洗。 您必须批量提交并确保您的日志位于快速的主轴阵列上。 启动一个事务,插入足够大的记录来填充日志页面(64kb)然后提交。 同样值得使用5-10个SqlCommands和连接的电池,并使用异步命令(带回调的BeginExecuteNonReader)并行启动多个插入,这样您就可以利用现在在网络往返和执行上下文准备中丢失的所有死区时间。

因此,单行约为8毫秒,而整个文件约为8毫秒。 公平?

数据库当然还有很多可能发生的事情:

  1. 解析,验证,执行SQL
  2. 计算任何索引的值
  3. 如果这是单个事务,则管理回滚日志
  4. 写入自己的文件

我假设您在本地运行,因此不需要包含网络延迟。

所以我猜想数据库速度较慢。 不过,我不会想到600K的速度。

你在做批量插入吗? 如果你已经存在,我会用它。

INSERT INTO dbo.NewTable(fields) 
SELECT fields 
FROM dbo.oldTable 
WHERE ...

在上面的示例中,您需要确保select语句中使用的表具有适当的索引...正确地将聚簇索引分配给最相关的字段。

如果select语句很慢,请检查执行计划以找到瓶颈。

MySQL对你帮助不大。 但是,SQL Server 2005及更高版本具有一些非常有趣的XML支持,可能会帮助您。 我建议您查看Updategrams,这项功能允许您提交要插入,更新或删除的一批数据。 这可能有助于您提高SQL Server的性能,因为您只需要发出一个语句而不是600,000个语句。 我不确定它是否会像写入原始文件一样快,但它应该比发出单个语句快得多。

你可以在这里开始学习更新图: http//msdn.microsoft.com/en-us/library/aa258671( SQL.80) .aspx

正如亚历克斯所说:使用SqlBulkCopy,在性能方面没有什么能比得上它。

使用起来有点棘手,示例代码请看这里:

http://github.com/SamSaffron/So-Slow/blob/1552b1293525bfe36f6c9b522e370de626ac6f05/Importer.cs

Ayende有一些有趣的代码可以批量处理这些ExecuteNonQuery情况。 Open Up Query Batching是介绍SqlCommandSet的介绍帖子,然后在There Be Dragons:Rhino.Commons.SqlCommandSet中发布代码。

如果您可以针对SQL2008进行优化,您还可以尝试闪亮的新表值参数。 这篇sqlteam文章是他们的一个很好的介绍。

您可能正在对数据库服务器一遍又一遍地运行命令,如果您构造包含多个插入的命令文本然后运行它,该怎么办?

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();

如果您不需要许多并发用户尝试使用MS-Jet,即“Microsoft Access”作为您的DBMS。 MSJet性能可以比SqlServer快约10倍。 顺便说一句,对于SqlServer来说,在50秒(12k / sec)内插入600k记录非常快。

我的猜测是你正在进行事务性插入:插入看起来像这样:

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驱动器上,那么性能会很差。

接下来,您也要设置表格。 你在桌子上有约束和索引吗? 你有没有其他记录,你有其他人在同一时间查询它吗? 如果是这样,请考虑为没有索引或约束的数据加载构建一个空表。 尽可能快地转储其中的所有数据,然后应用约束或索引,或将数据移动到其最终目标。

我的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();

想知道它会如何表现;)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM