ShardingJdbc 源码分析

31 阅读5分钟

ShardingJdbc 源码分析

1. 整体架构与核心模块

ShardingJdbc 的源码架构清晰,主要围绕数据分片的核心流程设计,包含以下几个核心模块:

应用程序 → JDBC代理层 → SQL解析引擎 → 路由引擎 → SQL改写引擎 → 执行引擎 → 结果归并引擎 → 应用程序

2. 核心类分析

2.1 JDBC代理层核心类

ShardingDataSource:核心入口类,实现了 JDBC 的 DataSource 接口

public class ShardingDataSource extends AbstractDataSourceAdapter {
    private final ShardingRuntimeContext runtimeContext;
    
    @Override
    public Connection getConnection() throws SQLException {
        // 创建分片连接
        return new ShardingConnection(getDataSourceMap(), runtimeContext, TransactionTypeHolder.get());
    }
}

ShardingConnection:封装了底层多个数据库连接的逻辑连接

public final class ShardingConnection extends AbstractConnectionAdapter {
    private final Map<String, DataSource> dataSourceMap;
    private final ShardingRuntimeContext runtimeContext;
    private final TransactionType transactionType;
    private final Map<String, Connection> cachedConnections = new LinkedHashMap<>();
    
    // 创建Statement
    @Override
    public Statement createStatement() throws SQLException {
        return new ShardingStatement(this, runtimeContext);
    }
    
    // 创建PreparedStatement
    @Override
    public PreparedStatement prepareStatement(final String sql) throws SQLException {
        return new ShardingPreparedStatement(this, sql, runtimeContext);
    }
}

2.2 SQL解析引擎

SQL解析是ShardingJdbc的核心功能,主要分为词法解析和语法解析两个阶段:

SQLParsingEngine:SQL解析的核心引擎类

public final class SQLParsingEngine {
    private final String databaseTypeName;
    private final SQLParserFactory sqlParserFactory;
    private final SQLStatementParser sqlStatementParser;
    
    // 核心解析方法
    public SQLStatementContext<?> parse(final String sql, final boolean useCache) {
        // 尝试从缓存获取解析结果
        // 执行SQL解析
        // 返回解析上下文
    }
}

词法解析

public final class LexerEngine {
    private final Lexer lexer;
    
    // 解析SQL为Token流
    public Token scan() {
        // 跳过空白字符
        // 识别关键字、标识符、字面量等
        // 返回Token
    }
}

语法解析:基于ANLTR(从3.0.x版本开始)实现,根据不同SQL类型(SELECT、INSERT、UPDATE、DELETE)进行相应的语法树构建。

2.3 路由引擎

ShardingSQLRouter:分片SQL路由器,负责根据分片规则计算SQL应该在哪些库表执行

public final class ShardingSQLRouter implements SQLRouter<ShardingRule> {
    @Override
    public RouteContext createRouteContext(final QueryContext queryContext, 
                                          final ShardingSphereDatabase database, 
                                          final ShardingRule rule,
                                          final ConfigurationProperties props,
                                          final ConnectionContext connectionContext) {
        // 获取SQL语句对象
        SQLStatement sqlStatement = queryContext.getSqlStatementContext().getSqlStatement();
        
        // 创建分片条件
        ShardingConditions shardingConditions = createShardingConditions(queryContext, database, rule);
        
        // 执行路由计算
        // 返回路由上下文
    }
}

ShardingRouter:核心路由实现类,包含多种路由策略

public final class ShardingRouter {
    // 单表路由
    public RouteResult route(final String sql, final List<Object> parameters) {
        // SQL解析
        // 路由计算
        // 返回路由结果
    }
}

2.4 SQL改写引擎

SQLRewriteEngine:负责将逻辑SQL改写为物理SQL

public final class SQLRewriteEngine {
    private final ShardingRule shardingRule;
    private final SQLParserEngine parserEngine;
    
    // 改写SQL
    public SQLRewriteResult rewrite(final String sql, final List<Object> parameters, 
                                   final boolean useCache, final ShardingConditions shardingConditions) {
        // 解析SQL
        // 执行改写
        // 返回改写结果
    }
}

2.5 执行引擎

ExecuteEngine:SQL执行引擎,负责并发执行SQL并管理连接

public final class ExecuteEngine {
    private final ExecutorEngine executorEngine;
    private final int maxConnectionsSizePerQuery;
    
    // 执行SQL
    public <T> List<T> execute(final Collection<ExecutionUnit> executionUnits, 
                              final ExecutionType executionType, 
                              final SQLExecutorCallback<T> callback) {
        // 并发执行SQL
        // 处理结果
        // 返回结果列表
    }
}

2.6 结果归并引擎

MergeEngine:结果归并引擎,将多个数据源的查询结果合并

public final class MergeEngine {
    private final ShardingRule shardingRule;
    private final SQLStatementContext<?> sqlStatementContext;
    
    // 创建结果归并器
    public ResultSetMerger merge(final List<ResultSet> resultSets, final SQLExecutor<?> sqlExecutor) {
        // 根据SQL类型创建不同的归并器
        // 返回结果归并器
    }
}

3. 分片策略实现

3.1 分片策略核心接口

ShardingStrategy:分片策略的核心接口

public interface ShardingStrategy {
    // 计算分片值
    Collection<String> doSharding(Collection<String> availableTargetNames, PreciseShardingValue shardingValue);
}

3.2 内置分片算法

InlineShardingStrategy:基于行表达式的分片策略

public final class InlineShardingStrategy implements PreciseShardingStrategy<Comparable<?>> {
    private final String shardingColumn;
    private final InlineExpressionParser inlineExpressionParser;
    
    @Override
    public String doSharding(final Collection<String> availableTargetNames, final PreciseShardingValue<Comparable<?>> shardingValue) {
        // 使用行表达式计算分片结果
        // 返回目标数据库或表名
    }
}

4. 执行流程源码分析

4.1 查询执行流程

ShardingPreparedStatement 为例,核心执行流程如下:

public final class ShardingPreparedStatement extends AbstractPreparedStatementAdapter {
    // SQL执行方法
    @Override
    public boolean execute() throws SQLException {
        // 1. SQL分片路由
        shard();
        
        // 2. 执行SQL
        clearPrevious();
        try {
            return execute0();
        } finally {
            // 3. 处理异常和清理
        }
    }
    
    // SQL分片路由
    private void shard() {
        // 从Connection获取运行时上下文
        ShardingRuntimeContext runtimeContext = connection.getRuntimeContext();
        
        // 创建SQL分片引擎
        PreparedQueryShardingEngine shardingEngine = new PreparedQueryShardingEngine(
                runtimeContext.getRule(), 
                runtimeContext.getProps(), 
                runtimeContext.getMetaData(), 
                runtimeContext.getParseEngine());
        
        // 执行分片路由
        sqlRouteResult = shardingEngine.shard(sql, getParameters());
    }
    
    // 执行SQL
    private boolean execute0() throws SQLException {
        // 根据路由结果执行SQL
        // 合并结果
        // 返回执行结果
    }
}

4.2 SQL解析流程

public final class SQLParsingEngine {
    // 解析SQL
    public SQLStatementContext<?> parse(final String sql, final boolean useCache) {
        // 尝试从缓存获取
        if (useCache) {
            ParsingResultCache parsingResultCache = ParsingResultCache.getInstance();
            SQLStatementContext<?> result = parsingResultCache.getSQLStatementContext(sql);
            if (null != result) {
                return result;
            }
        }
        
        // 创建SQL解析器
        SQLParser sqlParser = sqlParserFactory.newInstance(databaseTypeName, sql);
        
        // 执行解析
        SQLStatement sqlStatement = sqlParser.parse();
        
        // 创建解析上下文
        SQLStatementContext<?> result = createSQLStatementContext(sqlStatement);
        
        // 缓存结果
        if (useCache) {
            ParsingResultCache.getInstance().put(sql, result);
        }
        
        return result;
    }
}

4.3 路由流程

public final class ShardingRouter {
    // 路由方法
    public RouteResult route(final String sql, final List<Object> parameters) {
        // 1. SQL解析
        SQLStatementContext<?> sqlStatementContext = parserEngine.parse(sql, false);
        
        // 2. 创建路由上下文
        RouteContext routeContext = sqlRouter.createRouteContext(
                new QueryContext(sql, sqlStatementContext, parameters), 
                shardingSphereDatabase, 
                shardingRule, 
                configurationProperties, 
                connectionContext);
        
        // 3. 返回路由结果
        return new RouteResult(sqlStatementContext, routeContext);
    }
}

5. 读写分离实现

MasterSlaveRouter:读写分离路由器

public final class MasterSlaveRouter implements SQLRouter<MasterSlaveRule> {
    @Override
    public RouteContext createRouteContext(final QueryContext queryContext, 
                                          final ShardingSphereDatabase database, 
                                          final MasterSlaveRule rule,
                                          final ConfigurationProperties props,
                                          final ConnectionContext connectionContext) {
        // 判断SQL类型
        SQLStatement sqlStatement = queryContext.getSqlStatementContext().getSqlStatement();
        
        // 根据SQL类型选择数据源(主库或从库)
        String dataSourceName = rule.isMasterRoute(sqlStatement) ? 
                rule.getMasterDataSourceName() : 
                rule.getLoadBalancer().getDataSource(rule.getMasterDataSourceName(), rule.getSlaveDataSourceNames());
        
        // 创建路由上下文
        return new RouteContext(dataSourceName);
    }
}

6. 源码设计亮点

6.1 插件化架构

ShardingJdbc采用SPI机制实现插件化架构,主要体现在:

  • 分片算法插件化
  • SQL解析器插件化
  • 执行引擎插件化
  • 结果归并插件化

6.2 性能优化

  1. SQL解析缓存
public final class ParsingResultCache {
    private static final ParsingResultCache INSTANCE = new ParsingResultCache();
    private final LRUCache<String, SQLStatementContext<?>> cache = new LRUCache<>(SQL_PARSING_CACHE_SIZE);
    
    // 获取缓存的解析结果
    public SQLStatementContext<?> getSQLStatementContext(final String sql) {
        return cache.get(sql);
    }
    
    // 缓存解析结果
    public void put(final String sql, final SQLStatementContext<?> sqlStatementContext) {
        cache.put(sql, sqlStatementContext);
    }
}
  1. 并发执行
public final class ExecutorEngine {
    private final ExecutorService executorService;
    
    // 并行执行任务
    public <I, O> List<O> execute(final Collection<I> inputs, final ExecutorCallback<I, O> callback) {
        // 并发执行SQL
        // 收集结果
        // 返回结果列表
    }
}

7. 关键包结构

org.apache.shardingsphere.sharding
  ├── api            # API接口定义
  ├── config         # 配置相关
  ├── exception      # 异常处理
  ├── kernel         # 核心实现
  │   ├── parse      # SQL解析
  │   ├── route      # 路由实现
  │   ├── rewrite    # SQL改写
  │   ├── execute    # SQL执行
  │   └── merge      # 结果归并
  ├── route          # 路由引擎
  ├── rule           # 分片规则
  ├── strategy       # 分片策略
  └── util           # 工具类

8. 总结

ShardingJdbc通过精心设计的模块化架构,实现了高效、灵活的分库分表功能。其核心流程包括SQL解析、路由计算、SQL改写、并发执行和结果归并。源码设计体现了良好的扩展性和性能优化考虑,通过SPI机制支持各种自定义扩展,通过缓存和并发执行提高性能。

理解ShardingJdbc的源码架构,有助于我们更好地使用和扩展这个强大的分库分表中间件,也为我们设计类似的分布式系统提供了很好的参考。