ShardingSphere 的路由结果合并 Iterator 不主动触发 fail-fast,而是依赖底层集合(如 List 或 ResultSet)的迭代器在并发修改时抛出 ConcurrentModificationException;其归并引擎通常通过缓存快照或流式隔离避免该问题。

ShardingSphere 中的路由结果合并 Iterator 本身并不主动触发 Java 的 fail-fast 机制,但当它封装的底层集合(如分片查询返回的多个 ResultSet 或 List)在迭代过程中被并发修改时,可能间接暴露 fail-fast 行为——这本质上是委托给其内部使用的具体集合或迭代器的检查逻辑。
fail-fast 并非 ShardingSphere 自实现,而是依赖底层容器
ShardingSphere 的 MergeIterateResultSet 或 StreamMergeEngine 等合并逻辑,通常包装多个子结果集(如 ArrayList、LinkedList 或 JDBC ResultSet),而这些对象自身的迭代器在检测到结构修改(如 add/remove)时会抛出 ConcurrentModificationException。ShardingSphere 未重写这些集合的迭代器,因此 fail-fast 是继承而来,不是框架层新增的校验。
- 例如:若你在归并前手动修改了某一分片返回的
List<Object>(如调用list.remove(0)),随后用该 list 构造合并 iterator,遍历时大概率触发异常 - JDBC 的
ResultSet迭代器一般不支持 fail-fast(多数驱动不检查 modCount),但某些内存 ResultSet 实现(如 H2 的MemoryResultSet)可能模拟该行为
ShardingSphere 合并过程中的“安全边界”设计
框架在构造合并迭代器时,倾向于将原始结果转为不可变或快照式结构,降低外部干扰风险:
- 多数归并引擎(如
OrderByStreamMergeEngine)会先读取所有子结果并缓存为List<Object[]>或PriorityQueue,后续迭代基于副本而非原始引用 - 若使用流式归并(stream merge),则依赖各数据源 ResultSet 的独立游标,彼此隔离;此时单个 ResultSet 被外部修改,只影响自身迭代,不会导致整个合并器抛出 ConcurrentModificationException
- ShardingSphere 不允许用户直接操作合并器内部持有的集合引用,避免意外修改
如何避免误触 fail-fast 异常
关键在于区分“数据消费”和“数据修改”,确保合并阶段只读:
立即学习“Java免费学习笔记(深入)”;
- 不要在调用
ShardingSphereDataSource查询后,对返回的List结果做结构性修改再传入自定义归并逻辑 - 若需预处理数据,应创建新集合:
new ArrayList<>(originalList)或使用list.stream().map(...).collect(Collectors.toList()) - 多线程环境下,禁止多个线程共享同一个分片结果集合并同时读写;建议每个线程持有独立副本
- 排查异常堆栈时,重点看
ConcurrentModificationException的源头是否指向你代码中显式调用的add/remove,而非 ShardingSphere 内部类
定制归并逻辑时的注意事项
当你实现 MergeEngine 或扩展 QueryResult 处理链时,需自行保障迭代安全性:
- 避免在
next()方法中修改正在遍历的集合 - 如需动态过滤或转换,优先使用
Iterator<T>的hasNext()/next()协议,而非索引访问 + 修改原 list - 可考虑用
Collections.unmodifiableList()包装输入,提前拦截非法修改



















