learn-tech/专栏/ShardingSphere核心原理精讲-完/25归并引擎:如何理解流式归并和内存归并在复杂归并场景下的应用方式?.md
2024-10-16 06:37:41 +08:00

20 KiB
Raw Blame History

                        因收到Google相关通知网站将会择期关闭。相关通知内容
                        
                        
                        25  归并引擎:如何理解流式归并和内存归并在复杂归并场景下的应用方式?
                        承接上一课时的内容,今天我们继续介绍 ShardingSphere 中剩余的归并策略,包括分组归并、聚合归并和分页归并。

其中分组归并是最复杂的一种归并类型; 聚合归并是在分组归并的基础上追加的归并; 分页归并则是典型的通过装饰器模式实现的归并类型。

最复杂的归并:分组归并

在 ShardingSphere 的所有归并机制中,分组归并的情况最为复杂,它同样可以分为流式分组归并和内存分组归并两种实现方案。

其中,流式分组归并要求 SQL 的排序项与分组项的字段,以及排序类型必须保持一致,否则只能通过内存归并才能保证其数据的正确性。

因为分组归并非常复杂,所以,我们还是继续通过一个示例然后结合源码,给大家介绍分组归并的实现过程,先看这样一句 SQL

SELECT task_name, SUM(health_point) FROM health_task GROUP BY task_name ORDER BY task_name;

显然,上述 SQL 的分组项与排序项完全一致,都是用到了 task_name 列,所以取得的数据是连续的。这样,分组所需的数据全部存在于各个数据结果集的当前游标所指向的数据值,因此可以采用流式归并。

如下图所示,我们在每个 health_task 结果集中,根据 task_name 进行了排序:

我们先来看一些代码的初始化工作,回到 DQLMergeEngine找到用于分组归并的 getGroupByMergedResult 方法,如下所示:

private MergedResult getGroupByMergedResult(final Map<String, Integer> columnLabelIndexMap) throws SQLException { return selectSQLStatementContext.isSameGroupByAndOrderByItems() ? new GroupByStreamMergedResult(columnLabelIndexMap, queryResults, selectSQLStatementContext) : new GroupByMemoryMergedResult(queryResults, selectSQLStatementContext); }

可以看到这里有一个 isSameGroupByAndOrderByItems 判断,该判断就是用来明确分组条件和排序条件是否相同。根据前面的分析,如果分组条件和排序条件相同,则执行流式分组归并方式 GroupByStreamMergedResult否则使用内存分组归并 GroupByMemoryMergedResult。

我们以流式归并为例来介绍 ShardingSphere 中的分组归并实现机制,在对代码进行详细展开之前,我们还是需要先从感性认识上明确流式分组归并具体要执行的步骤。这里仍然使用一系列的示意图来进行说明。

现在,我们已经在每个 health_task 结果集中根据 task_name 进行了排序,所以 health_task0、health_task1、health_task2 中的“task1”都排到了最前面也就是队列的第一个元素。

第一次 next 调用

这样当进行第一次 next 调用时,排在队列首位的 health_task0 将会被弹出队列并且将分组值同为“task1”其他结果集中的数据一同弹出队列。然后在获取了所有的 task_name 为“task1”的 health_point 之后,我们进行了累加操作。

所以在第一次 next 调用结束后,取出的结果集是 “task1” 的分数总和,即 46+43+40=129如下图所示

第二次 next 调用

与此同时所有数据结果集中的游标都将下移至“task1”的下一个不同的数据值并且根据数据结果集当前游标指向的值进行重排序。在上图中我们看到第二个“task2”同时存在于 health_task0 和 health_task1 中这样包含名字为“task2”的相关数据结果集则排在的队列的前列。

当再次执行 next 调用时,我们获取了 “task2” 的分数并进行了累加,即 42+50=92如下图中所示

对于接下去的 next 方法,我们也是采用类似的处理机制,分别找到这三种 health_task 表中的“task3”“task4”“task5”等数据记录并依次类推。

有了对流式分组归并的感性认识之后,让我们回到源代码。我们先来看代表结果的 GroupByStreamMergedResult我们发现 GroupByStreamMergedResult 实际上是继承了上一课时中介绍的用于排序归并的 OrderByStreamMergedResult因此也用到了前面介绍的优先级队列 PriorityQueue 和 OrderByValue 对象。

但考虑到需要保存一些中间变量以管理运行时状态GroupByStreamMergedResult 中添加了如下所示的代表当前结果记录的 currentRow 和代表当前分组值的 currentGroupByValues 变量:

private final List