gpt4 book ai didi

delphi - TClientDataSet 对字符串字段使用了太多内存

转载 作者:行者123 更新时间:2023-12-03 15:01:13 25 4
gpt4 key购买 nike

当我试图支持 this question 时,我被触发问这个问题。具有 MCVE。

我最近开始注意到 TClientDataSet 很快就会耗尽内存。我在生产中遇到了一个问题,它无法加载大约 60.000 个数据集,这对我来说似乎低得惊人。客户端数据集通过提供程序与 ADODataSet 连接,加载良好。我单独运行该查询并将结果输出到 CSV,这给了我一个 < 30MB 的文件。

所以我做了一个小测试,我可以在客户端数据集中加载大约 165K 条记录,其中有一个大小为 4000 的字符串字段。该字段的实际值只有 3 个字符,但这并不'似乎对结果并不重要。

看起来每条记录至少占用这 4000 个字符。 4000 x 2 字节 x 165K 记录 = 1.3GB,因此开始接近 32 位内存限制。如果我把它变成一个备忘录字段,我可以轻松添加 500 万行。

program ClientDataSetTest;
{$APPTYPE CONSOLE}
uses SysUtils, DB, DBClient;

var
c: TClientDataSet;
i: Integer;
begin
c := TClientDataSet.Create(nil);
c.FieldDefs.Add('Id', ftInteger);
c.FieldDefs.Add('Test', ftString, 4000); // Actually claims this much space...
//c.FieldDefs.Add('Test', ftMemo); // Way more space efficient (and not notably slower)
//c.FieldDefs.Add('Test', ftMemo, 1); // But specifying size doesn't have any effect.
c.CreateDataSet;

try
i := 0;
while i < 5000000 do
begin
c.Append;
c['Id'] := i;
c['Test'] := 'xyz';
c.Post;

if (i mod 1000) = 0 then
WriteLn(i, c['Test']);

Inc(i);
end;

except
on e: Exception do
begin
c.Cancel;
WriteLn('Error adding row', i);
Writeln(e.ClassName, ': ', e.Message);
end;
end;

c.SaveToFile('c:\temp\output.xml', dfXML);
Writeln('Press ''any'' key');
ReadLn;
end.

所以问题本身有点广泛,但我希望有一个解决方案,并且能够通过更有效地使用字符串空间来加载更大的数据集。该字段之所以很大,是因为它们可以包含注释。对于大多数记录来说,这些记录都是空的或短的,因此这是对空间的极大浪费。

  • TClientDataSet 是否可以配置为以不同方式处理此问题?我浏览了它的属性,但找不到任何与此相关的内容。
  • 可以通过使用不同的字段类型来解决吗?我想到了 ftMemo,但它还有一些其他缺点,例如大小不用于截断,以及一些显示问题,例如 TDBGrid 将其显示为 (MEMO),而不是实际值。
  • 是否有 TClientDataSet 的直接替代品可以解决这个问题?它不仅仅涉及内存部分,还涉及通过 TProvider 与 ADO 组件进行通信,这是我在这个项目中使用它的主要方式,因此不是任何内存数据集都可以解决这个问题。

对于最后一点,我碰巧发现 this question ,隐藏在评论中,提到了 vgLib,但我发现的只是损坏的链接,我什至不知道它是否可以解决这个问题。显然,MidasLib 的 C++ 代码现在已经可用,但由于它是 1.5MB 的晦涩代码,我认为在深入研究之前可能值得在这里询问一下。 ;)

最佳答案

blob 字段(备注)和常规字段存储和检索数据的方式有所不同。 Blob 字段不在记录缓冲区中存储数据(请参阅TBlobField.GetDataSize),并且在存储或检索该数据时使用一组不同的方法。

每条记录的大小通过调用 TField.GetDataSize 返回。对于 TStringField,这是所需的字符串大小 + 1。

TCustomClientDataSet.InitBufferPointers 使用此值作为 FRecBufSize 值计算的一部分,该值用作为 TCustomClientDataSet 中的每个记录分配的内存大小。 AllocRecordBuffer.

那么,回答你的问题:

  • TClientDataSet 无法配置为以任何不同的方式执行此操作。
  • 它可以通过其他字段类型来解决,但它们必须从 TBlobField 派生。缓冲区大小是预先分配的,因此常规字段不能根据其内容包含不同的大小。
  • 我不确定替代品是否会减少。 Dev Express 有一个 dxMemData,但我不知道它是否会遇到同样的问题,或者是否是替代品。

关于delphi - TClientDataSet 对字符串字段使用了太多内存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55375774/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com