如何在 Oracle 中查找 SQL 查询中返回的记录的内存大小?

Posted

技术标签:

【中文标题】如何在 Oracle 中查找 SQL 查询中返回的记录的内存大小?【英文标题】:How to find Memory SIZE of records returned in a SQL Query in Oracle? 【发布时间】:2019-10-25 06:48:46 【问题描述】:

我正在查询返回 10,000 条记录。

SELECT * FROM employee WHERE id < 11000;

返回的数据包含 85 列(varchar、日期、编号)。 (另外我有另一个类似的查询获取数据仅包含 10 列(varchar)。)

Oracle 有没有办法找到这个结果集的数据大小?像加载的数据将是 100 MB 或 200 MB

需求:实际上我需要加载内存中的所有记录;对于这些记录,在 Java 中进行一些处理。所以我需要使用 Oracle 中的一些先决条件来检查数据的大小,或者您可以建议以任何其他适当的方式来检查数据的大小? (我有生产访问权限。所以我将在检查数据大小后实现逻辑)。

此预检查只是为了避免 java 中的内存不足异常。

如果我复制整个数据并将其保存在文件中,对于 10,000 条具有 8 列的记录,它仅显示 604 KB。内存中也会有同样的情况吗?

【问题讨论】:

“我需要将所有记录加载到内存中;这些记录需要在 Java 中进行一些处理” 嗯……你打算在之后将这些记录写回数据库吗?这个处理? 嗯,varchar 列可以包含不同长度的值(因此得名),因此将在 Java 中创建的字符串也将具有不同的内存要求。话虽如此,可能很难计算所需的确切内存量,因为在多个位置(如结果集,在您的应用程序中,每个引用都已经使用了一些内存,即使引用同一个对象也是如此) ,一些驱动程序内部位置等)。 ... 但是您应该能够估计可能需要的内存,并且在大多数情况下这可能就足够了(如果您在严格的限制下操作,则可能并非如此但这些限制在业务应用程序中很少存在):获取结果将包含的每种数据类型的大小(如IntegerDate 等)以及字符串的最大长度。将所有这些加起来并乘以 10k,您应该已经得到了合理的估计。 是的,就是这样。请注意,Java 对象有很多内部字段和引用,因此它们需要比基本原始类型更多的内存(例如,Integer 至少需要 16 个字节:12 个用于对象标头,4 个用于 int 值) .此外,将数据写入文本文件会改变大小,因为这取决于编码(Latin-1 将使用每个字符 1 个字节,而 Java 字符串已经使用每个字符 2 个字节)以及是否将小整数转换为需要更少内存的文本也是。 "如果我复制整个数据并将其保存在文件中,对于 8 列的 10,000 条记录,它仅显示 604 KB。" - 假设您在这里使用的是 Latin-1,没有列分隔符,也没有压缩。这意味着您平均每行有 (604 * 1024 - 10000 / 10000) = 60.8 个字节(那 - 10000 是因为您至少需要行分隔符)。这意味着每列平均需要 7.6 个字节。让我们将其四舍五入到 8,因此您的 80000 列仅用于字符数据(8 * 80000 * 2)就需要 1280000 字节(1.2 MB)。 【参考方案1】:

通常,您可以使用 Java 的检测功能来确定运行时的内存消耗。有关这方面的一些信息,请查看此处:

In Java, what is the best way to determine the size of an object? https://www.baeldung.com/java-size-of-object

但是,由于各种原因,终止实际内存消耗并不总是那么容易,其中一些原因是:

框架、库甚至 JVM 都可能创建数据副本或缓存并重用它 查询可能会返回不同大小的结果,尤其是在使用可变长度列类型(如 VARCHAR)时。您必须读取该数据以确定相应对象的实际大小 某些对象可能被多个其他对象引用,因此它们的大小可能被错误地包括在内(例如,如果使用了一些可能计入对象大小但实际上不会增加​​的枚举常量,因为它很可能已经被无论如何加载)。

此外,在大多数业务应用程序中,您无需费心确定一段代码导致的确切内存消耗。同样有各种原因,例如:

内存很便宜,因此如果遇到问题,通常(至少暂时)增加可用内存比(微)优化一段代码更容易。 由于情况不断变化(例如活跃用户数量、数据变化等),系统使用情况和负载通常无法预测 JVM 通常能够有效地使用垃圾收集来为其他事情回收内存。

这并不意味着您不应该考虑内存使用情况,例如你真的需要一次内存中的所有这些 10k 行吗?您需要这些数据多长时间?您在用它做什么?

话虽如此,粗略估计内存消耗通常很有帮助,如果查询可能返回大量字符串,您应该估计最坏的情况,即假设最大长度的字符串。

为此,您需要了解行将包含的内容,例如无论是整数 IntegerLong 还是 BigInteger 实例,还是可能有多少列。此外,您至少需要了解数据类型的内存要求,即我们不考虑任何缓存、复制、ResultSet 的开销等。

Java 对象的大小取决于各种因素,例如您正在使用哪个 JVM,无论是 32 位还是 64 位 JVM 等。各种来源表明,可以从 Object 标头(通常被称为 12 字节大小)和大小计算出对象的内存消耗对象的字段。

使用它,我们将假设 Integer 的大小为 16 字节(12b 标头和 4b int),Date 将是 24 字节(12b 标头,8b fastTime 和 4b cdate 参考),字符串将是 12b 标头、4b char[] 引用、8b 其他字段、12h char[] 标头和字符本身的 2*length 个字节(或总共 36 + 2 * 长度)。

因此,假设您的 85 列分为 20 个整数、10 个日期和 55 个最大长度为 256 字节的字符串。一行需要至少 20 * 16 + 10 * 24 + 55 * 548 = 30700 字节。因此,10k 行需要 307000000 字节或大约 300 MB(当所有字符串都处于最大长度时)。

如果我复制整个数据并将其保存在文件中,对于 8 列的 10,000 条记录,它仅显示 604 KB。

让我们也分解一下:

604 KB 为 618496 字节(1024 为 1 KB) 除以 10k,平均每行得到 61.8 个字节 除以 8 得到每列 7.7 个字节(如果我们不考虑任何行或列分隔符) 让我们将其四舍五入为每列 8 个字节,并假设您的文本文件是 Latin-1 编码的(因此每个字符 1 个字节),因此每个文本列平均有 8 个字符,这很短

在更简单的计算中,如果我们使用与上述相同的假设,604KB 意味着您的数据将包含大约 604k 个字符,在 Java 中,仅字符数据就需要 1208k 字节(或大约 1.2MB)。再加上 80k 字符串的开销,即 36 字节 * 80k,大约多出 2.8 MB,因此数据需要大约 4 MB 的内存。

【讨论】:

以上是关于如何在 Oracle 中查找 SQL 查询中返回的记录的内存大小?的主要内容,如果未能解决你的问题,请参考以下文章

Oracle SQL:如何从组中查找记录

如何在 Oracle SQL 中查找最具体的匹配行

Oracle:查找多个查询运行的总查询运行时间

oracle 查找某字段中含有回车换行的记录,请问怎么写SQL?

Oracle SQL:ORA-01427:单行子查询返回多于一行

在另一个字段Oracle SQL中包含的一个字段中查找文本