FDQuery引起内存溢出异常

7
我有一个包含3百万条记录的Firebird数据库。我的FetchOptionsRowsetSize:=1000FetchAll被禁用。我正在本地主机上工作。
我没有浏览页面时的问题,但当我点击DBNavigator中的“转到最后一条记录”按钮时,它会抛出“内存不足”异常。
如果我将UniDirectional属性设置为True,就不会有问题。然而,移动到最后一条记录会导致应用程序冻结30-40秒钟。
问题是什么,解决方案是什么?

这里没有明确的问题。可能只是我,但我不知道你在问什么。为什么你的内存用完了?看起来你已经找到了解决方法。 - nil
2
此时 - 是指什么时间?当您调用 Last 还是当您调用 FetchAll1 时?应用程序会冻结30-40秒 - 当然会,您可能正在下载300万条记录,并且在数据库访问中产生了大量的堆内存分配和释放,甚至在数据库可视化组件中更多。这是徒劳的任务。没有人能够在工作中阅读300万行。这样做毫无意义。 - Arioch 'The
欢迎来到SO!请参观The Tour了解Stack Overflow的简要介绍,然后阅读Asking以了解您可以提出哪些问题以及如何表达您的问题。请注意,问题必须清晰明确 - Tom Brunberg
仅考虑一下...通常打开一个有3M行的数据集并不是正确的做法...特别是如果这发生在附加了UI控件的情况下(就像你的情况一样)。如果您正在执行批量操作,则可能有意义(但Unidirectional可以帮助)。但是,没有用户会愉快地导航3M条记录。您应该考虑创建更好的UI,以帮助用户缩小数据集范围,找到他真正想处理的数据。 - Frazz
3个回答

3
问题在于您的方法实际上会消耗掉所有可用的内存,当您收到“内存不足”错误时会得到适当的通知。所有记录都从服务器获取并保存在内存中。操作系统无法为您的应用程序分配更多内存。

怎么办?只需避免加载数百万条记录。无论如何,您的用户都无法遍历/浏览/检查来自巨大数据集的每个记录。

需要考虑的选项:

  • 仅检索用户所需的记录,限制/过滤数据集
  • 仅从服务器源数据中选择必要的字段,而不是全部
  • 使用单向传输以避免在单次传输场景中进行本地缓存

2
解决方案是使用TFDTable数据集,而不是默认的TFDQuery。TFDTable数据集支持实时数据窗口模式。在此模式下,数据集是双向的,因此可以与可视化组件一起使用,例如TDBGrid。与TFDQuery相比,区别在于旧记录被丢弃。每个时间点,数据集仅在内存中保留2 * FetchOptions.RowsetSize条记录(默认值为2 * 50 = 100条记录),即表数据的一个窗口。当应用程序浏览表数据时,FireDAC会自动滚动或定位实时数据窗口到所需位置。这提供了以下好处:
  • 最小化内存使用,允许您处理大容量数据,类似于单向数据集。
  • 实现双向导航,与单向数据集相反。
  • 提供始终更新的数据,减少刷新数据集的需要。
  • 不会延迟获取所有结果集数据,以执行排序、记录定位、跳转到最后一个记录等操作。 Filter属性、范围过滤、IndexFieldNames和IndexName属性、Locate和Lookup方法、键定位、设置RecNo、设置书签等操作,都是通过附加SELECT命令或为主SELECT命令设置附加短语来完成的。更改Filter、IndexFieldNames或IndexName后,数据集中的当前位置也可能会发生改变。为保留当前位置,请在更改之前保存书签,并在更改后恢复它。

在LDW模式下,Filter属性值被直接替换为WHERE短语。 按设计,LDW模式总是将ORDER BY短语应用于SELECT命令。正确使用LDW的关键要求是:

  • 表必须具有唯一或主键。详情请参见唯一标识字段。
  • 服务器端排序排序规则和客户端排序排序规则必须相同。否则,TFDTable可能会产生重复行并引发“唯一键违规”错误。

尽管在LDW模式下,FireDAC最大程度地减少了生成和执行的SQL命令数量,但它仍然比TFDQuery产生更重的数据库负载。因此,应用程序开发人员应仔细选择何时使用TFDTable和LDW模式。

设置LDW模式

当满足以下所有条件时,将使用LDW模式:

  • CachedUpdates默认为False。
  • FetchOptions.Unidirectional默认为False。
  • FetchOptions.CursorKind设置为ckAutomatic或ckDynamic(ckAutomatic是默认值)。
  • 表具有主键或唯一键。

否则,将使用标准模式。

更多信息可以在 Embarcadero 文档中找到: http://docwiki.embarcadero.com/RADStudio/Rio/zh-cn/Browsing_Tables_(FireDAC)#Live_Data_Window_Mode


1

当FireDAC查询设置为单向时,它会在访问记录后从内存中删除这些记录。这确保不会发生内存溢出。

移动到最后一条记录时的时间延迟是因为需要访问每个中间记录,这显然需要一些时间。


谢谢您的回答,但第三方数据库工具不会冻结或延迟。它们可以立即定位到最后一条记录。这些工具是如何实现的呢? - Niyazi Korkmaz
3
如果你只从一张有主键的表中选择数据,可以使用TFDTable代替TFDQuery,并将CachedUpdates设置为false,FetchOptions.Unidirectional和FetchOptions.CursorKind设置为ckAutomatic或ckDynamic。这样,TFDTable就在实时数据窗口模式下操作,对于大型表格的性能更好。 - Uwe Raabe
谢谢你,但不幸的是你的解决方案没有起作用。 问题仍然存在。 - Niyazi Korkmaz
第三方数据库工具不会冻结或延迟,并立即在完全相同的情况下定位,@NiyaziKorkmaz?我的意思是,如果我理解正确,您的数据集中有3百万条记录。那真的很多。 - nil

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接