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 性能优化
- 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);
}
}
- 并发执行:
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的源码架构,有助于我们更好地使用和扩展这个强大的分库分表中间件,也为我们设计类似的分布式系统提供了很好的参考。