delphi - TClientDataSet 对字符串字段使用过多内存
问题描述
当我试图用 MCVE支持这个问题时,我被触发提出这个问题。
我最近开始注意到 TClientDataSet 很快就会耗尽内存。我在生产中遇到了一个问题,它无法加载大约 60.000 的数据集,这对我来说似乎低得惊人。客户端数据集通过提供程序与 ADODataSet 连接,该 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 组件进行通信,这是我在这个项目中使用它的主要方式,所以没有任何内存数据集可以解决问题。
对于最后一点,我碰巧发现了这个问题,隐藏在评论中,提到了 vgLib,但我发现的只是链接断开,我什至不知道它是否能解决这个问题。显然 MidasLib 的 C++ 代码现在可用,但由于它是 1.5MB 的晦涩代码,我认为在深入研究之前可能值得在这里询问。;)
解决方案
每当我在 CDS 中需要相当长的“字符串”字段时,我倾向于创建一个备忘录。除了前面提到的显示问题(可以很轻松地解决),几乎没有其他限制,所以我有自定义 cds 后代。hyperbase(不是 vglib)内部字符串格式是相同的,所以在这方面它不会改变任何东西。顺便说一句,有允许自定义和选择目标字段类型映射的 DAC(例如 firedac)。不确定是否可以修补/增强 ado 组件以实现类似的功能。此外,iirc fireac 数据集可以选择控制内部字符串字段布局(“内联”行内缓冲区或只是指向动态分配的缓冲区的指针),但不是 cds 的 1:1 替代品。
推荐阅读
- c# - ASP.NET Core - 创建基本控制器端点 - 当子控制器端点也存在时
- python - 如何使用 Python `subprocess` 在 Windows 上检查外部应用程序的状态?
- vba - 从 VBA IDE 运行 VBA 例程?
- android - 使用 Whatsapp 共享视频时出现“不支持文件”错误
- python - 从 playsound 导入 playsound playsound('C:\\Users\\Home\\Desktop\\DESKTOP\\Python\\play.mp3')
- angular - 如何在不使用 concatMap 的情况下延迟 observable?
- python - os.nice() 等效于 Windows 操作系统
- java - 将 Java 服务器数据发送到 React 前端
- python - 在将数据填充到矩阵 python 时降低时间复杂度
- python-3.x - 如何评估熊猫的多列?