在.NET中的ETL期间避免OutOfMemoryException的策略

时间:2011-04-28 15:57:51

标签: c# .net data-warehouse etl

我编写了一个执行ETL过程的ETL过程。 ETL过程需要处理超过1亿或整行,为期2年的记录。为了避免内存不足问题,我们将数据加载到每7天一次。对于每个块进程,它会加载所有必需的引用数据,然后进程打开一个sql连接并逐个加载源数据,转换它,并将其写入数据仓库。

按块处理数据的缺点是速度慢。

对于大多数表来说,这个过程一直很好,但是有一个表我仍然遇到内存不足。该过程加载了太多参考数据。我想避免将数据缩减到3天,以便它具有良好的性能。

我可以使用其他策略来避免OutOfMemoryException吗?

例如,本地数据库,将引用数据写入文件,生成另一个.NET进程以在Windows中保存更多内存,使用CLR存储过程来执行ETL ...

环境:Windows 7 32位操作系统。 4 GB的RAM。 SQL Server标准版。

唯一的解决方案是使用存储过程并让SQL Server处理ETL。但是,我试图避免它,因为该程序也需要支持Oracle。 我尝试的其他性能改进是添加索引以改进加载查询。创建自定义数据访问类以仅加载必要的列,而不是将整个行加载到内存中。

由于

3 个答案:

答案 0 :(得分:1)

在不知道如何精确处理数据的情况下很难说,但是在任何情况下都可以实现的一个天真的解决方案是使用64位操作系统并将应用程序编译为64位。在32位模式下,.NET堆只会增长到大约1.5GB,这可能会限制你。

答案 1 :(得分:1)

我知道它的老帖子,但是对于那些寻找更好的点来使用编程语言编写数据操作的人们。

我不确定您是否考虑研究ETL工具如何执行其数据加载操作并在代码中复制类似的策略。

一个这样的建议,并行数据管道。在这里,每个管道将基于源数据的分区对单个块执行ETL。例如,您可以考虑并行生成不同星期数据的生成过程。这仍然无法在单个过程中解决您的内存问题。尽管可以在单个进程内的堆内存分配达到极限的情况下使用。这对于随机访问并行读取数据也很有用。尽管需要一个主过程来协调和完成该过程,作为单个ETL操作。

我假设您在转换中执行了许多查找操作,然后才将数据最终写入数据库。假设主交易表很大,参考数据很小。您需要专注于数据结构的操作和算法。以下是相同的一些技巧。在编写算法时选择最适合的套件之前,请参考数据的特征。

通常,查找数据(参考数据)存储在缓存中。选择一个对读取和搜索操作有效的简单数据结构(例如数组列表)。如果可能的话,请按您要加入的键对数组进行排序,以提高搜索算法的效率。

在转换任务中有不同的查找操作策略。在数据库世界中,您可以将其称为联接操作。

合并联接算法: 当源已经按连接属性键排序时,这是理想的选择。排序合并算法的关键思想是首先按join属性对关系进行排序,以便交错的线性扫描将同时遇到这些集合。有关示例代码,https://en.wikipedia.org/wiki/Sort-merge_join

嵌套加入: 就像嵌套循环一样,其中将外循环索引的每个值都用作内循环索引的限制(或起点或任何适用的值),并在语句之后执行相应的操作内循环。因此,基本上,如果外循环执行R次,而内循环每次执行S次,则嵌套循环的总成本或时间复杂度为O(RS)。

当在联接列上建立索引时,嵌套循环联接可提供有效的访问。此外,在许多小事务中,例如只影响一小部分行的事务,索引嵌套循环连接要远远优于sort -merge联接和hash联接

我仅描述可以在您的查找操作中考虑的两种方法。在ETL中要记住的主要思想是关于查找和检索元组(已设置)以进行进一步操作。搜索将基于键,并且生成的事务键将提取所有记录(投影)。进行此操作,并通过一次读取操作从文件中加载行。如果您不需要转换操作的所有记录,这是更多建议。

另一个非常昂贵的操作是写回数据库。可能倾向于同时处理提取,转换和加载一行。考虑一下可以向量化的操作,您可以在其中将其与数据结构操作一起批量执行。例如,对多维矢量执行lambada操作,而不是一次循环每一行,并跨给定行的所有列执行转换和操作。然后,我们可以将此向量写入文件或数据库。这样可以避免内存压力。

答案 2 :(得分:0)

这是一个非常老的问题,它更多是一个设计问题,除非有更具体的细节,否则我肯定会有很多解决方案。

最终,我用Merge编写了SQL存储过程来处理ETL处理数据类型,该数据类型花费了很长时间来处理C#应用程序。此外,业务需求发生了变化,因此我们放弃了对Oracle的支持,仅支持64位服务器,从而降低了维护成本并避免了ETL内存不足的问题。

此外,只要有机会提高查询性能,我们就会添加许多索引。

ETL进程不是按天范围进行分块,而是按计数(5000)对数据进行分块并提交每个事务,这减小了事务日志文件的大小,如果ETL失败,则该过程仅需要回滚子集数据。

最后,我们实现了缓存(键,值),以便将ETL日期范围内的频繁引用数据加载到内存中,以减少数据库查询。