【问题标题】:Strategy to avoid OutOfMemoryException during ETL in .NET.NET 中 ETL 期间避免 OutOfMemoryException 的策略
【发布时间】:2011-08-14 20:35:24
【问题描述】:

我编写了一个执行 ETL 过程的 ETL 过程。 ETL 流程需要为 2 年的记录处理超过 100+ 百万或总共的行。为了避免内存不足的问题,我们将数据加载分块为每 7 天一次。对于每个chunk进程,加载所有需要的参考数据,然后进程打开sql连接,将源数据一一加载,转换,写入数据仓库。

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

这个过程对于大多数表都运行良好,但有一个表我仍然遇到内存不足的情况。进程加载了太多参考数据。我想避免将数据分块到 3 天,以使其具有良好的性能。

是否有任何其他策略可以用来避免 OutOfMemoryException?

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

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

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

谢谢

【问题讨论】:

  • 一种选择是为“参考数据”创建一个固定大小的缓存。当您的程序需要参考数据时,它会在缓存中查找。如果它不存在,它会从您的 SQL 服务器加载它。这只有在参考数据中存在局部性时才有效。

标签: c# .net data-warehouse etl


【解决方案1】:

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

【讨论】:

  • 当然!到目前为止,最划算的是切换到 64 位,甚至可能投资双倍的内存。尝试在小内存空间中设计解决方案的成本远远超过在 8 或 16 GB RAM 上花费几美元。
  • 我在 64 位上测试过,肯定能解决问题。不幸的是,限制在客户端的机器上。他们只运行 32 位操作系统。我将不得不与 IT 部门交谈,看看是否可以升级到 64 位操作系统。
【解决方案2】:

我知道它的旧帖子,但它适用于寻找更好点以使用编程语言编写数据操作的人。

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

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

我假设您在最终将数据写入数据库之前在转换中执行了大量查找操作。假设主事务表很大,参考数据很小。您需要专注于数据结构的操作和算法。下面有一些相同的提示。在编写算法时,请先参考数据的特征,然后再选择最适合的套件。

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

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

合并加入算法: 当源已经按连接属性键排序时是理想的。 sort-merge 算法的关键思想是首先通过连接属性对关系进行排序,这样交错的线性扫描就会同时遇到这些集合。示例代码,https://en.wikipedia.org/wiki/Sort-merge_join

嵌套连接: 像嵌套循环一样工作,其中外部循环索引的每个值都被视为内部循环索引的限制(或起点或任何适用的),并在以下语句上执行相应的操作内循环。所以基本上,如果外循环执行 R 次,而内循环每次执行 S 次,那么嵌套循环的总成本或时间复杂度为 O(RS)。

当表在连接列上建立索引时,嵌套循环连接提供了有效的访问。此外,在许多小型事务中,例如仅影响一小部分行的事务中,索引嵌套循环连接远远优于排序合并连接和哈希连接

我只描述了在您的查找操作中可以考虑的两种方法。在 ETL 中要记住的主要思想是查找和检索元组(按集合)以进行进一步操作。搜索将基于密钥,生成的交易密钥将提取所有记录(投影)。使用它并在一次读取操作中从文件中加载行。如果您不需要转换操作的所有记录,这更多是建议。

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

【讨论】:

    【解决方案3】:

    这是一个非常古老的问题,它更像是一个设计问题,我相信有很多解决方案,除非我更具体的细节。

    最后,我使用 Merge 编写了 SQL 存储过程来处理 ETL 过程,因为这种数据类型需要很长时间才能处理 C# 应用程序。另外,业务需求发生了变化,我们放弃了对Oracle的支持,只支持64位服务器,降低了维护成本,避免了ETL内存不足的问题。

    此外,每当我们发现有机会提高查询性能时,我们就会添加许多索引。

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-20
      • 1970-01-01
      • 1970-01-01
      • 2011-08-18
      • 2013-10-23
      相关资源
      最近更新 更多