SQL Server和SqlDataReader - 万亿条记录 - 内存

8

我从未尝试过这个,所以我不知道是否会遇到内存问题。

但是 SqlDataReader 是否能够读取万亿条记录?它全部是流式的,对吗?我对 SQL/TDS 协议背后的运作还有些陌生。

更新:请将 Trillion 翻译为非常大的数字。我可能应该说 10 亿或者 1 亿。


5
你打算阅读一万亿条记录吗?还是只是出于兴趣? - gbn
3个回答

15

有一些细节。

  • SqlDataReader通常会将整个行读入内存并缓存。这包括任何BLOB字段,因此您可能会在内存中缓存几个2GB的字段(XML、VARBINARY(MAX)、VARCHAR(MAX)、NVARCHAR(MAX))。如果这些字段是一个问题,那么您必须向ExecuteReader传递CommandBehavior.SequentialAccess并使用SqlClient特定类型的流功能,例如SqlBytes.Stream

  • 连接在SqlDataReader完成之前处于忙碌状态。这会创建事务问题,因为您将无法在同一事务中在数据库中进行任何处理,因为连接正忙。尝试打开不同的连接并在同一事务中注册将失败,因为禁止回路分布式事务。解决方案是使用MARS。您可以通过在连接上设置MultipleActiveResultSets=True来实现这一点。这允许您在数据读取器仍处于活动状态时在相同的连接上发出命令(典型的获取-处理-获取循环)。仔细阅读Christian Kleinerman的链接,确保您理解MARS和事务周围的问题和限制,它们非常微妙和反直觉。

  • 客户端中的长时间处理将阻塞服务器。您的查询仍将执行所有这些时间,并且当通信管道填满时,服务器将不得不暂停它。查询消耗worker(如果它具有并行计划,则可能需要多个),而工作线程是服务器中非常稀缺的资源(它们大致相当于线程)。 您将无法承受许多客户端以自己的节奏处理巨大的结果集。

  • 事务大小。在一个事务中处理万亿条记录永远不会起作用。日志将必须增长以容纳整个事务,并且不会截断和重用VLF,从而导致巨大的日志增长。

  • 恢复时间。如果在第9990亿条记录处处理失败,它将不得不回滚所有已完成的工作,因此需要另外'12'天才能回滚。


1
非常好的信息。+1 如果数据只需要最终一致,事务在系统中扮演什么角色?您认为批处理1000或10000个数据的正确方式是什么?(请参见对Jon Skeet的评论) - BuddyJoe
1
创建可以安全恢复的批处理的正确方法取决于实际执行的任务。一个微不足道的例子是拥有一个带有“当前”聚集键值的表。在事务中,您从表中获取该值,按聚集键选择下一个10k行,处理它们,在表中更新当前键值,提交。反复洗涤、循环和重复。 - Remus Rusanu

12

是的,那样可以流式处理...但我不认为你应该尝试这样做。

即使你每秒钟能读取一百万条记录(这听起来对我来说不太可能),你仍需要12天才能读取一万亿条记录......那是很多工作,有风险在中途失去。

现在我意识到你可能并不真的想读取一万亿条记录,但我的观点是,如果你可以将你的"大量"工作分成逻辑批次,那可能是一个好主意。


所以我的最初问题是ADO.NET和SQL Server的最佳批处理策略是什么...那么一次处理1000条记录的最佳方法是什么。假设你正在进行MapReduce类型的活动。我意识到还有其他工具可以做到这一点(开源和商业),但如果你所在的公司不允许你使用它们...对我来说没有任何好处。(除了试图借鉴一些想法) - BuddyJoe
12天+1的观点很好。也许我选的数字太高了。 - BuddyJoe
说实话,最好的批处理策略将取决于任务的确切性质。即使在以后运行查询,您能否可靠地将其拆分成批次?您能否提前将其拆分成批次,并为不同的计算机分配不同的批次?是否有其他内容正在写入此数据?是否存在适当的索引?基本上,这是一个找出一种将数据拆分为可查询和高效形式的方法的情况。 - Jon Skeet
那些问题是我曾经苦恼过的。当我处于处理过程中时,其他人可能会向数据写入。我没有一个好的“快照”策略。这就是我真正努力理解的。 - BuddyJoe
起初可能只有一个服务器(4个核心)处理数据。到年底可能会增加到两个或三个服务器。考虑在这个项目中使用一些 F#。它似乎非常适合这个项目。 - BuddyJoe

1

是的 - 这可能需要一些时间(只要您的SQL不会做任何傻事尝试拍摄快照或其他任何事情),但如果您的服务器可以流式传输,SqlDataReader就不应该有内存使用问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接