跳转到内容

归并引擎

物理执行返回的是多个 QueryResult,但 JDBC 使用者期待一个 ResultSet。归并引擎通过 SPI 选择具体 ResultMerger,没有特殊规则时退化为透明结果,并可在最后叠加结果装饰器。

先给答案:物理执行返回的是多个 QueryResult,但 JDBC 使用者期待一个 ResultSet

Section titled “先给答案:物理执行返回的是多个 QueryResult,但 JDBC 使用者期待一个 ResultSet”

物理执行返回的是多个 QueryResult,但 JDBC 使用者期待一个 ResultSet。归并引擎通过 SPI 选择具体 ResultMerger,没有特殊规则时退化为透明结果,并可在最后叠加结果装饰器。 正文沿“结果模型 -> 实现拆解 -> 为什么要让归并结果实现接口”展开:先确认入口和状态归属,再跟踪控制流或数据流的推进,最后落到对外可观察的结果。

主要失效边界集中在“ORDER BY 跨分片、聚合函数、大结果集”这些场景。它们破坏的是容量、顺序、并发或生命周期前提;排查时应先确认状态是否仍由正确对象持有,再核对推进条件和清理路径。

QueryResult[0..n]
|
+-- matching ResultMergerEngine -> MergedResult
| |- StreamMergedResult
| |- MemoryMergedResult
| `- DecoratorMergedResult
`-- no merger -> TransparentMergedResult
|
v
JDBC ResultSet adapter
  • MergeEngine#merge 先执行具体归并,找不到实现时使用第一个结果的透明包装:infra/merge/src/main/java/org/apache/shardingsphere/infra/merge/MergeEngine.java:81、:82。
  • executeMerge 遍历结果处理引擎,只对 ResultMergerEngine 建立 ResultMerger:MergeEngine.java:87、:92。
  • 归并实例收到数据库、协议、规则、属性、语句上下文和连接上下文:MergeEngine.java:93、:94。
  • 归并完成后再按顺序应用 ResultDecorator:MergeEngine.java:102、:107。
  • MergedResult 只暴露 next、取值和流读取等稳定操作,隐藏具体归并算法:infra/merge/src/main/java/org/apache/shardingsphere/infra/merge/result/MergedResult.java:28、:31。
  • StreamMergedResult 以当前 QueryResult 提供流式访问,避免默认复制全部行:infra/merge/src/main/java/org/apache/shardingsphere/infra/merge/result/impl/stream/StreamMergedResult.java:34、:41、:46。

替代方案:把所有分页、排序、聚合、流式读取逻辑硬编码进 JDBC ResultSet。 为什么不行:JDBC 适配器会同时承担协议兼容、资源关闭和数据库差异,算法难以独立测试;不同 SQL 类型还需要完全不同的消费策略。 证据:MergedResult 只定义统一结果游标合同,MergeEngine 通过 ResultMergerEngine 和 ResultDecorator SPI 选择实现:infra/merge/src/main/java/org/apache/shardingsphere/infra/merge/MergeEngine.java:89、:102。

场景 现象 原因 规避
ORDER BY 跨分片 返回顺序不稳定 每个物理结果只保证局部顺序 使用排序归并并确认比较列
聚合函数 count/sum 结果错误 不能简单拼接结果集 使用对应聚合 ResultMerger
大结果集 内存峰值高 选择了 Memory 归并 优先流式算法,限制分页窗口
结果为空 直接访问当前结果异常 QueryResult 已耗尽或为空 正确处理 next() 和资源生命周期

“稳定最小接口 + 策略实现 + 后置装饰器”适合日志聚合、分页查询、事件流合并和多来源 API 聚合。透明实现作为默认路径,可以降低单源场景的额外成本。

面试锚点

  • TransparentMergedResult 什么时候被使用?
  • Stream 与 Memory 归并的内存和延迟取舍是什么?
  • 为什么归并后还要有 ResultDecorator?