首页 > 解决方案 > FireDAC CopyDataSet 和 CopyRecord 内存丢失是否有解决方案?

问题描述

我编写了一个将一系列 Paradox 表迁移到 PostgreSQL 数据库的应用程序。不幸的是,将 Paradox 表中的数据导入 PostgreSQL 表时,内存消耗会增加,并且对于其中一个较大的表(100 万条记录),导入会因“内存不足”异常而崩溃。当我使用 CopyDataSet 为整个表导入数据时,以及当我使用 CopyRecord 逐条导入数据时,都会发生此崩溃。

我一直在尝试了解这种内存泄漏的来源。本来我以为是FireDAC ODBC驱动的使用才是根源。或者更具体地说,我认为 Microsoft ODBC 驱动程序是源。为了测试这一点,我尝试使用本地 FireDAC Postgres 驱动程序从一个非常大的 PostgreSQL 表导入另一个 PostgreSQL 表。但是内存泄漏仍然存在。

我正在使用单向游标(FetchOptions.Unidirectional := True)打开源表,文档状态将丢弃已在前向扫描中处理的记录,但泄漏仍然存在。在所有情况下,我都使用 FDQuery。我尝试使用启用了 Live Data Windows (LDW) 模式的 FDTable,但 Microsoft ODBC 驱动程序和 FDTable 之间存在不兼容,因此这不是一个选项。

这是我用于此测试的基本代码:

FDQuery1.SQL.Text := 'SELECT * FROM sourcetab;';
FDQuery1.FetchOptions.Unidirectional := True;
FDQuery1.FetchOptions.Mode := fmOnDemand;
FDQuery1.Open();
FDQuery2.SQL.Text := 'SELECT * FROM destinationtab WHERE False;';
FDQuery2.Open;
try
  while not FDQuery1.Eof do
  begin
    FDQuery2.Append;
    FDQuery2.CopyRecord( FDQuery1 );
    FDQuery2.Post;
    FDQuery1.Next;
  end;
finally
  FDQuery1.Close;
  FDQuery2.Close;
  ShowMessage( 'Done' );
end;

我最终做的是每 1000 个追加关闭并重新打开目标表。关闭目标表会阻止内存使用量的线性增加。现在 while 循环看起来像这样:

while not FDQuery1.Eof do
begin
  FDQuery2.Append;
  FDQuery2.CopyRecord( FDQuery1 );
  FDQuery2.Post;
  if (FDQuery1.RecNo mod  1000) = 0 then
  begin
    FDQuery2.Close;
    FDQuery2.Open;
  end;
  FDQuery1.Next;
end;

我尝试了 FDQueries 的许多其他配置,只是为了得到相同的泄漏结果。

有没有其他人注意到这种行为,有没有人知道比关闭和重新打开目标查询的开销更少的解决方案?

我正在使用任务管理器监控内存使用情况。一旦内存使用量接近 2 GB,程序就会崩溃。正确关闭目标数据集会释放大量内存。

操作系统:Windows 10 64 位,平台:Delphi Rio 10.3.3,数据库:PostgreSQL 11,目标平台:32 位(无 64 位 Paradox ODBC 驱动程序)

标签: delphimemory-leaksfiredaccopydataset

解决方案


在使用与您相同的方法(包括关闭/打开解决方法)时,我遇到了类似的情况。

内存增加的原因(它没有内存泄漏!)是附加到目标查询内部记录缓冲区的记录量。虽然单向有助于限制源查询的内存,但它不能阻止目标查询在每个记录增加内存后添加记录。由于目标查询只附加记录,这些记录永远不会被使用或搜索,这完全是浪费。

您可以使用 close 和 open 来保持您的解决方法,这对我来说也非常有效。

作为替代方案,您可以使用简单的 INSERT 查询,但需要更多编码才能将源字段放入目标参数中。

编辑:实际上,当Unidirectional为 True 时,查询可以知道是否可以在AppendPost之后释放缓冲区中的所有先前记录,但由于这是FetchOption在Post期间可能不会考虑。


推荐阅读