打开大型 Access .MDB 文件时如何防止 OutOfMemory 异常?

How to prevent OutOfMemory exception when opening large Access .MDB files?

我正在尝试读取由另一个应用程序生成并存储在 Microsoft Office Access .MDB 文件中的数据。根据模型的大小(在其他应用程序中),某些特定 table 中的记录数量可能从几千条到超过一千万条不等。在一个查询中打开整个 table 可能会导致大文件出现内存不足异常。所以我根据一些条件拆分 table 并在不同的查询中读取每个部分。但问题是关于中等大小的文件,这些文件可以在一个查询中读取得更快,没有例外。

那么,我走对了吗?我可以用另一种方式解决 OutOfMemory 问题吗?是否可以根据记录数选择提到的策略之一(1 查询或 N 查询)?

顺便说一下,我使用的是 DelphiXE5 和 Delphi 的标准 ADO 组件。我需要 table 的全部数据,不需要连接到其他 table。我正在通过代码创建 ADO 组件,它们没有连接到任何可视控件。

编辑:

嗯,看来我的问题还不够清楚。下面是一些更详细的内容,实际上是对评论中提出的问题或建议的回答:

这个 .mdb 文件没有保存真实的数据库;它只是结构化数据,所以没有写入新数据,没有事务,没有用户交互,没有服务器,什么都没有。第三方应用程序使用 Access 文件导出其计算结果。这些文件的总大小通常约为几百 MB,但它们可以增长到 2 GB。现在我需要在开始自己的计算之前将这些数据加载到 Delphi 数据结构中,因为在这些计算期间没有地方等待 I/O。

我无法为 x64 编译这个项目,它极度依赖于一些与主执行程序共享相同内存管理器的旧 DLLtable,并且它们的作者永远不会发布 x64 版本。公司还没有决定更换他们,近期不会改变。

而且,您知道,支持人员更愿意告诉我们“解决这个问题”,而不是要求两千名客户“购买更多内存”。所以我必须对内存使用非常吝啬。

现在我的问题是:TADODataSet 是否为获取如此大量的数据提供了更好的内存管理?是否有任何 属性 阻止 DataSet 一次获取所有数据?

当我调用 ADOTable1.open 时,它开始分配内存并等待获取整个 table,正如预期的那样。但是在 for 循环中读取所有这些记录将需要一段时间,并且不需要拥有所有这些数据,另一方面,读取后不需要在内存中保留记录,因为没有行搜索。这就是为什么我将 table 与一些查询分开。现在我想知道 TADODataSet 是否可以解决这个问题,或者我正在做的是唯一的解决方案。

我做了一些尝试和错误,提高了读取数据的性能,包括内存使用和运行时间。我的测试用例是一个有超过 5,000,000 条记录的 table。每条记录有 3 个字符串字段和 8 个双精度值。没有索引,没有主键。我使用 GetProcessMemoryInfo API 来获取内存使用情况。

初始状态

Table.Open: 33.0 s | 1,254,584 kB  
Scrolling : +INF s | I don't know. But allocated memory doesn't increase in Task Manager. 
Sum       : -      | -

DataSet.DisableControls;

Table.Open: 33.0 s | 1,254,584 kB  
Scrolling : 13.7 s | 0 kB
Sum       : 46.7 s | 1,254,584 kB  

DataSet.CursorLocation := clUseServer;

Table.Open: 0.0 s  | -136 kB  
Scrolling : 19.4 s | 56 kB
Sum       : 19.4 s | -80 kB  

DataSet.LockType := ltReadOnly;

Table.Open: 0.0 s  | -144 kB  
Scrolling : 18.4 s | 0 kB
Sum       : 18.5 s | -144 kB  

DataSet.CacheSize := 100;

Table.Open: 0.0 s  | 432 kB  
Scrolling : 11.4 s | 0 kB
Sum       : 11.5 s | 432 kB  

我也查了Connection.CursorLocarion,Connection.IsolationLevel,Connection.Mode DataSet.CursorTypeDataSet.BlockReadSize 但他们没有做出明显的改变。

我也尝试使用 TADOTableTADOQueryTADODataSet,与 Jerry 在评论中所说 here 不同,ADOTable 和 ADOQuery 的性能都比 ADODataSet 好。

分配给 CacheSize 的值应该针对每个案例来决定,并不是任何磨碎的值都会导致更好的结果。