我在数据库中有大约6千万条记录,必须处理所有这些记录。因此,我们的想法是使用c#代码读取数据,处理数据然后将其放回数据库中。数据不会出现在同一个表中 - 涉及多个表。
我想看看最好的做法是什么?我应该一次在数据集中读取100K记录,然后处理每条记录,然后使用批量插入数据库然后读取下一组吗?
答案 0 :(得分:2)
通常,绝对最快的方法是在SQL批处理中执行服务器上的所有操作。
如果你坚持使用客户端,那么单独的线程读取和写入可能比使用一个线程更快。要读取和写入的线程数取决于硬件和正在执行的操作
编辑:澄清方法。
检索数据并将数据发送到sql server是网络IO绑定和进程外。这意味着在读取和发送数据时,应用程序会花费时间等待数据通过网络从磁盘进入内存。让我们假设需要1个小时来检索数据。 10分钟处理和1小时将数据发送回数据库。所以整个过程需要2小时10分钟。
如果将其拆分为三个线程,1个读取器,1个处理器,1个更新程序。你可以把它降到接近1小时。如果您编写好应用程序,可以添加其他线程进行读取,处理和写入,但是由于共享缓存行,网卡如何响应大量并发请求等原因,您可能会对结果感到失望。
此外,当您使用DataAdapter填充数据集时,在填充完成之前无法触及任何数据。另一方面,如果您是DataReader,则可以在第一行完成时开始使用数据。这意味着您不必担心一次限制为100K。
答案 1 :(得分:2)
不要靠近DataSet或DataAdapter!
要获取数据,请使用DataReader - 通过调用ExecuteReader,通过SqlCommand使用SQL文本或Stored Proc调用。然后,您可以一次从DataReader中检索记录,而不包含DateSet,Entity Framework或Linq to SQL或NHibenate附带的任何对象跟踪包 - 所有这些框架都添加了层以允许您执行对象和更改跟踪 - 您不需要,只会为您带来开销。
当您将结果写回数据库时,通过SqlBulkCopy执行此操作,启用TableLock并使用“恢复模型”设置数据库属性,而不是“完整”。确保禁用目标表上的约束,并且未定义任何索引(如果需要,请删除并在末尾重新创建)。
当发送回SQL Server时,SqlBulkCopy会自己进行批处理,如果您确定指定了BatchSize(默认值是一批中的所有内容)。您可能还希望在SqlBulkCopy上设置UseInternalTransaction,以便每个批处理都在自己的事务中完成 - 这将进一步减少事务日志的使用。
读者和作者线程可能有所帮助,或者可能没有,我没有说明差异。如果您提到的“第三方流程”非常耗时,您可能还需要一个或多个处理线程或其他一些机制。
可以在一个线程上完成所有操作,一次一条记录,并且可以非常快(取决于您正在进行的处理的成本)。
如果您需要使用多个线程,请不要在此之间交换单个记录,因为您将丢失太多CPU周期的线程切换:将其分成“合理”批次。 “合理”可能介于1k到100k之间,具体取决于记录大小和处理过程。甚至可以让Parallels为你做这件事。
鉴于您说涉及多个表,可能只是为每个源表启动一个线程可能运行良好,并且锁定写入SqlBlukCopy对象以进行同步。