2

谁能告诉我聚合函数是如何在 SQL 数据库中实现的,例如:Oracle 或 SQL Server。

我的意思是,当 select 子句中存在聚合函数时,这些数据库是否使用一些内部数据结构或算法。

我问这个的原因是因为我在 java ArrayList 中有 100,000 条记录,当我尝试对所有值求和时大约需要 1 分钟,但是当相同的 100,000 条记录存储在 DB 中并且我使用 sum(column_nm) 它时执行时间几乎是 1/4。

我想以类似的方式提高我的 java 代码性能,为此我想知道 SQL 聚合函数的内部结构。

谢谢。

4

5 回答 5

2

有一个非常简单的解释,为什么 java 代码要慢得多:

您正在使用 ArrayList,因此我假设您将 Integer-Objects 放在那里。在某些堆栈中,它们确实比 C 中的整数有很大的开销。其次,当您将它们相加并为每个部分和创建另一个 Integer 时,您的 GarbageCollector 会吃掉所有性能。

如其他答案所述,

  1. DB 将使用直接数学处理器访问仅在寄存器中添加整数 - 不能更快。
  2. 好的数据库不会只迭代,而是映射 + 减少总和、最小值或最大值等聚合。因此,它们获得了多处理器的好处,几乎忽略了 I/O 延迟。

为了您在代码中解决它:使用 int[]

 int[] parts;
 sum=0;
 for (int i:parts) {
   sum+=i;
 }

您可能想要测试,如果根据您的处理器数量拆分(映射)数组并将其与 Future 并行化是否有用 - 取决于您的数据大小。

于 2013-02-26T10:12:21.467 回答
2

尽管这与内部定义的聚合的工作方式不完全匹配,但在 SQL Server 中,您可以创建用户定义的聚合。看看这样的聚合必须定义哪些方法可能会很有启发性:

  • Init

查询处理器使用此方法来初始化聚合的计算。对于查询处理器正在聚合的每个组,都会调用一次此方法。查询处理器可以选择重用聚合类的相同实例来计算多个组的聚合。Init 方法应根据此实例的先前使用执行任何必要的清理,并使其能够重新启动新的聚合计算。

  • Accumulate

...查询处理器使用此方法来累积聚合值。对于正在聚合的组中的每个值,都会调用一次。只有在聚合类的给定实例上调用 Init 方法后,查询处理器才会调用它。此方法的实现应该更新实例的状态以反映传入的参数值的累积。

  • Merge

此方法可用于将此聚合类的另一个实例与当前实例合并。查询处理器使用此方法来合并聚合的多个部分计算。

  • Terminate

该方法完成聚合计算并返回聚合结果。...

从 和 的描述中MergeTerminate我们可以推断服务器可能在单个组内并行执行多个部分聚合。一旦发生了这些并行累积中的每一个,所有结果将Merge在最终调用Terminate类的一个实例之前生成最终的聚合结果。

因此,实现加速(如果可能)的一种明显方法是并行化累积阶段。

于 2013-02-26T07:39:48.940 回答
1

性能差异仅仅是因为要计算 SUM,您不需要将所有数据同时存储在内存中。

当您直接向数据库发出请求 SUM 的查询时,它可以从磁盘读取每条记录,将运行总计累积到内存中的单个变量中,然后读取下一条记录 - 它永远不需要将内存中的所有记录保存在同时。更重要的是,它不需要通过网络将这些记录发送到任何其他服务器进行处理 - 它只需要在最后将生成的 SUM 作为单个数字发送。

此外,由于整体上的 SUM 等于整体的任何不同子集的 SUM,所以 SUM 可以并行化 - 例如,如果数据是分区的,数据库可以发出多个查询以在不同的会话中运行,每个查询将对其部分数据求和,然后控制会话可以简单地对每个分区的结果进行求和。

当您在 Java 程序中使用数组计算总和时,它必须首先向数据库发出查询,询问它需要的所有数据;所有数据都需要从数据库传输到应用服务器,并且需要分配内存来存储所有数据。只有在那之后,您的程序才会遍历内存中的数组并计算总和;然后,它可能需要从内存中释放 Array。

如果数据量低,性能差异可能微不足道。但是,如果数量很大,则可以预期差异会非常显着。

于 2013-02-26T08:22:28.670 回答
0

聚合通常只是迭代结果集,然后它们执行聚合,无论是求和、平均还是计数等。

如果您在谈论操作的复杂性,它几乎总是 O(n),其中 n 是结果集中用于简单聚合的记录数。

我不明白为什么在 java 中需要更长的时间,因为您的数组将被实例化到主内存中,这比从磁盘读取要快,就像 RDBMS 那样。老实说,来自 RDBMS 的聚合应该比 arraylist 聚合稍慢。

为了对此进行扩展,如果您想要一个特定条目的一行(带有 PK 或索引),对于一个数组列表来说它是 O(1),对于一个具有适当索引的 RDBMS 来说是 O(1)(对于一个标准的链表,获取该行将是 o(n),但与聚合的 arraylist 相同)。遍历整个数据集(无论是数组还是表),执行聚合几乎总是 O(n)。

于 2013-02-26T05:54:51.570 回答
0

有趣的问题。

一个写得很好的 rdbms 是数以千计的博士数学家和数据库专家工作时间的结晶。您模仿 MSSQL 或 postgressql 的性能的尝试令人钦佩,但在风车上倾斜(如果您不熟悉唐吉诃德,则阅读是徒劳的)。

对 rdbms 的一个常见误解是关系意味着相关表。相关实际上是指数学关系。基本上 - rdbms 专注于集合论。即使有很好的 rdbms,开发人员也可以通过逐行计算来破坏性能,而不是使用固有的原生集合。这实际上是对您所遇到的性能差异的恰当比较。

如果您仅限于在 java 而不是 db 中执行此计算,您应该考虑优化数据结构(最小数据类型)和循环效率。你仍然无法与 sql server 或 postgres 竞争。如果您确实需要改进的性能,可能值得将项目存储在数据库中并从 java 调用它们。

于 2013-02-26T09:34:20.500 回答