ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

ShardingSphere执行引擎解析:JDBC适配与分片查询实现

ShardingSphere执行引擎解析:JDBC适配与分片查询实现

1. ShardingSphere执行引擎深度解析:从Statement到DataSource的完整链路

在分布式数据库中间件领域,ShardingSphere的执行引擎设计堪称典范。本文将深入剖析ShardingStatement和ShardingPreparedStatement的实现原理,揭示其如何通过适配器模式完美兼容JDBC规范,并构建完整的SQL执行链路。对于需要处理分库分表场景的Java开发者而言,理解这套执行模型至关重要。

1.1 ShardingStatement的核心架构

ShardingStatement作为StatementExecutor的直接使用者,其类结构设计体现了典型的装饰器模式。核心字段包括:

  • connection:持有ShardingConnection实例,用于获取运行时上下文
  • statementExecutor:实际执行SQL语句的核心组件
  • sqlRouteResult:存储路由引擎的处理结果
  • currentResultSet:维护当前结果集状态

构造函数中完成了StatementExecutor的初始化:

public ShardingStatement(final ShardingConnection connection, final int resultSetType, final int resultSetConcurrency, final int resultSetHoldability) { super(Statement.class); this.connection = connection; statementExecutor = new StatementExecutor(resultSetType, resultSetConcurrency, resultSetHoldability, connection); }

类继承体系采用三层结构:

  1. 顶层:AbstractUnsupportedOperationStatement(处理不支持的JDBC操作)
  2. 中间层:AbstractStatementAdapter(实现基础Statement功能)
  3. 实现层:ShardingStatement(添加分片逻辑)

这种分层设计使得核心功能、兼容性处理和业务逻辑各司其职,是值得学习的架构模式。

1.2 执行流程深度剖析

以executeQuery方法为例,完整的执行链路包含五个关键阶段:

1.2.1 预处理阶段
clearPrevious(); // 清理历史执行状态

该方法会重置statementExecutor中的临时变量,确保每次执行都是独立上下文。在实际开发中,这种显式的状态清理比隐式自动清理更可靠,能避免内存泄漏和状态污染。

1.2.2 路由分片阶段
shard(sql); // 执行路由引擎

路由引擎的工作流程:

  1. 从Connection获取ShardingRuntimeContext
  2. 创建SimpleQueryShardingEngine实例
  3. 调用shard方法获取SQLRouteResult

路由结果包含:

  • 逻辑SQL与真实SQL的映射关系
  • 数据源分片信息
  • 需要执行的真实SQL列表
1.2.3 执行器初始化
initStatementExecutor(); // 初始化执行器

该方法完成两项重要工作:

  1. 根据路由结果准备物理Statement
  2. 通过replayMethodForStatements重放历史方法调用

重放机制是ShardingSphere的亮点之一,它通过反射记录并重放JDBC方法调用,确保装饰器模式不会丢失原始Statement的行为。

1.2.4 结果归并阶段
MergeEngine mergeEngine = MergeEngineFactory.newInstance( connection.getRuntimeContext().getDatabaseType(), connection.getRuntimeContext().getRule(), sqlRouteResult, connection.getRuntimeContext().getMetaData().getRelationMetas(), statementExecutor.executeQuery());

归并引擎采用工厂模式创建,根据数据库类型自动选择最优归并策略。归并过程会对来自不同分片的ResultSet进行:

  • 内存排序
  • 分组聚合
  • 分页裁剪
  • 去重处理
1.2.5 结果集包装
result = getResultSet(mergeEngine);

最终结果会被包装为ShardingResultSet,对外提供统一的访问接口,隐藏底层多数据源的复杂性。

1.3 ShardingPreparedStatement的特殊设计

ShardingPreparedStatement在Statement基础上增加了参数处理能力,其核心字段包括:

private final List<SetParameterMethodInvocation> setParameterMethodInvocations; private final List<Object> parameters;

参数处理采用命令模式,通过SetParameterMethodInvocation封装参数设置操作:

public final class SetParameterMethodInvocation extends JdbcMethodInvocation { @Getter private final int index; // 参数位置 @Getter private final Object value; // 参数值 public void changeValueArgument(final Object value) { getArguments()[1] = value; // 支持参数值修改 } }

这种设计实现了:

  1. 延迟绑定:先记录参数设置操作,实际执行时才应用
  2. 参数修改:支持运行时调整参数值
  3. 调用追踪:完整记录参数设置历史

类继承体系比Statement更复杂:

AbstractUnsupportedOperationPreparedStatement ↑ AbstractStatementAdapter ↑ AbstractShardingPreparedStatementAdapter ↑ ShardingPreparedStatement

这种设计源于PreparedStatement需要同时继承Statement功能和支持参数处理,在复杂系统开发中,这种多层适配器模式能有效解决接口兼容问题。

2. 执行引擎的入口实现:从Connection到DataSource

2.1 ShardingConnection的核心职责

作为执行引擎的入口点,ShardingConnection主要提供:

  1. Statement创建工厂方法
public Statement createStatement(int resultSetType, int resultSetConcurrency) { return new ShardingStatement(this, resultSetType, resultSetConcurrency); }
  1. 分布式事务管理
public void commit() throws SQLException { if (TransactionType.LOCAL == transactionType) { super.commit(); // 本地事务 } else { shardingTransactionManager.commit(); // 分布式事务 } }

事务处理采用策略模式,根据transactionType自动选择本地或分布式事务实现。这种设计使得事务类型的切换对业务代码完全透明。

2.2 ShardingDataSource的初始化过程

作为最顶层的入口,ShardingDataSource的构造函数完成了关键初始化:

public ShardingDataSource(final Map<String, DataSource> dataSourceMap, final ShardingRule shardingRule, final Properties props) throws SQLException { super(dataSourceMap); runtimeContext = new ShardingRuntimeContext(dataSourceMap, shardingRule, props, getDatabaseType()); }

其中ShardingRuntimeContext包含:

  • 数据源元信息
  • 分片规则
  • SQL解析引擎
  • 其他运行时组件

DatabaseType的自动识别逻辑:

private DatabaseType createDatabaseType(final DataSource dataSource) throws SQLException { try (Connection connection = dataSource.getConnection()) { return DatabaseTypes.getDatabaseTypeByURL(connection.getMetaData().getURL()); } }

通过JDBC URL识别数据库类型,支持MySQL、PostgreSQL、Oracle等主流数据库。这种SPI扩展机制使得新增数据库支持非常方便。

2.3 工厂类的最佳实践

ShardingDataSourceFactory展示了优雅的工厂模式实现:

public static DataSource createDataSource( final Map<String, DataSource> dataSourceMap, final ShardingRuleConfiguration shardingRuleConfig, final Properties props) throws SQLException { return new ShardingDataSource(dataSourceMap, new ShardingRule(shardingRuleConfig, dataSourceMap.keySet()), props); }

使用示例:

// 配置分片规则 ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration(); TableRuleConfiguration tableRuleConfig = new TableRuleConfiguration("user", "ds${0..1}.user${0..1}"); shardingRuleConfig.getTableRuleConfigs().add(tableRuleConfig); // 创建DataSource DataSource dataSource = ShardingDataSourceFactory.createDataSource( dataSourceMap, shardingRuleConfig, new Properties());

这种声明式的配置方式与Spring Boot的配置风格高度一致,降低了使用门槛。

3. 执行引擎的实践启示

3.1 适配器模式的工程实践

ShardingSphere的适配器实现给出了优秀示范:

  1. 抽象层处理不兼容操作
public abstract class AbstractUnsupportedOperationStatement implements Statement { public final void setPoolable(boolean poolable) throws SQLException { throw new SQLFeatureNotSupportedException("setPoolable"); } }
  1. 适配层实现标准接口
public abstract class AbstractStatementAdapter extends AbstractUnsupportedOperationStatement { public void setFetchSize(int rows) throws SQLException { recordMethodInvocation("setFetchSize", new Class[]{int.class}, new Object[]{rows}); for (Statement each : getRoutedStatements()) { each.setFetchSize(rows); } } }
  1. 实现类添加业务逻辑
public class ShardingStatement extends AbstractStatementAdapter { public ResultSet executeQuery(String sql) throws SQLException { // 分片执行逻辑 } }

3.2 分布式事务的透明化处理

通过TransactionTypeHolder实现事务上下文的传递:

public class TransactionTypeHolder { private static final ThreadLocal<TransactionType> CONTEXT = new ThreadLocal<>(); public static TransactionType get() { return CONTEXT.get(); } }

这种基于ThreadLocal的设计使得事务类型可以在调用链中无缝传递,对业务代码无侵入。

3.3 性能优化关键点

  1. 连接获取优化
public Connection getConnection() throws SQLException { return new ShardingConnection(getDataSourceMap(), runtimeContext, TransactionTypeHolder.get()); }

每个ShardingConnection只维护逻辑连接,物理连接按需获取,避免资源浪费。

  1. 批量执行支持
public class BatchPreparedStatementExecutor { public int[] executeBatch() throws SQLException { // 将批量操作按数据源分组执行 } }

批量操作自动路由到对应数据源,减少网络往返次数。

4. 常见问题排查指南

4.1 路由结果不符合预期

排查步骤:

  1. 检查ShardingRule配置是否正确
  2. 验证分片键是否存在于SQL中
  3. 调试SimpleQueryShardingEngine的shard方法

4.2 结果归并内存溢出

解决方案:

  1. 使用流式归并替代内存归并
  2. 添加合理的LIMIT分页
  3. 调大jdbcStreamFetchSize参数

4.3 分布式事务超时

处理建议:

  1. 检查事务超时时间设置
  2. 避免跨过多数据源的事务
  3. 考虑使用最终一致性方案

5. 设计模式应用总结

ShardingSphere执行引擎中蕴含的设计模式精华:

设计模式应用场景实现示例
适配器模式JDBC接口兼容AbstractStatementAdapter
装饰器模式功能增强ShardingStatement
工厂模式对象创建ShardingDataSourceFactory
策略模式分布式事务切换ShardingTransactionManager
命令模式参数设置操作封装SetParameterMethodInvocation

这些模式的应用不是孤立的,而是相互配合形成完整的解决方案。在实际开发中,我们可以借鉴这种模式组合的思路,而不是机械地套用单个模式。

返回列表