Sharding-JDBC 源码分析 —— SQL 路由(二)之分库分表路由.docx

上传人:rrsccc 文档编号:10161191 上传时间:2021-04-25 格式:DOCX 页数:48 大小:805.03KB
返回 下载 相关 举报
Sharding-JDBC 源码分析 —— SQL 路由(二)之分库分表路由.docx_第1页
第1页 / 共48页
Sharding-JDBC 源码分析 —— SQL 路由(二)之分库分表路由.docx_第2页
第2页 / 共48页
Sharding-JDBC 源码分析 —— SQL 路由(二)之分库分表路由.docx_第3页
第3页 / 共48页
Sharding-JDBC 源码分析 —— SQL 路由(二)之分库分表路由.docx_第4页
第4页 / 共48页
Sharding-JDBC 源码分析 —— SQL 路由(二)之分库分表路由.docx_第5页
第5页 / 共48页
点击查看更多>>
资源描述

《Sharding-JDBC 源码分析 —— SQL 路由(二)之分库分表路由.docx》由会员分享,可在线阅读,更多相关《Sharding-JDBC 源码分析 —— SQL 路由(二)之分库分表路由.docx(48页珍藏版)》请在三一文库上搜索。

1、1. SQL 路由结果2. 路由策略 x 算法3. SQL 路由器内容顺序如编号。Sharding-JDBC 正在收集使用公司名单:传送门。传送门Sharding-JDBC 也会因此,能够覆盖更多的业务场景。传送门登记吧,骚年!传送门SQL 路由大体流程如下:2. SQLRouteResult经过SQL解析、SQL路由后,产生SQL路由结果,即 SQLRouteResult。根据路由结果,生成SQL,执行SQL。 sqlStatement:SQL语句对象,经过SQL解析的结果对象。 executionUnits:SQL最小执行单元集合。SQL执行时,执行每个单元。 generatedKeys:

2、插入SQL语句生成的主键编号集合。目前不支持批量插入而使用集合的原因,猜测是为了未来支持批量插入做准备。3. 路由策略 x 算法ShardingStrategy,分片策略。目前支持两种分片:分片资源:在分库策略里指的是库,在分表策略里指的是表。【1】 计算静态分片(常用)/ ShardingStrategy.java/* 计算静态分片.* param sqlType SQL语句的类型* param availableTargetNames 所有的可用分片资源集合* param shardingValues 分片值集合* return 分库后指向的数据源名称集合*/public Collecti

3、on doStaticSharding(final SQLType sqlType, final Collection availableTargetNames, final CollectionShardingValue shardingValues) Collection result = new TreeSet(String.CASE_INSENSITIVE_ORDER); if (shardingValues.isEmpty() Preconditions.checkState(!isInsertMultiple(sqlType, availableTargetNames), INSE

4、RT statement should contain sharding value.); / 插入不能有多资源对象 result.addAll(availableTargetNames); else result.addAll(doSharding(shardingValues, availableTargetNames); return result;/* 插入SQL 是否插入多个分片* param sqlType SQL类型* param availableTargetNames 所有的可用分片资源集合* return 是否*/private boolean isInsertMultip

5、le(final SQLType sqlType, final Collection availableTargetNames) return SQLType.INSERT = sqlType & availableTargetNames.size() 1; 插入SQL 需要有片键值,否则无法判断单个分片资源。(Sharding-JDBC 目前仅支持单条记录插入)【2】计算动态分片/ ShardingStrategy.java/* 计算动态分片.* param shardingValues 分片值集合* return 分库后指向的分片资源集合*/public Collection doDyna

6、micSharding(final CollectionShardingValue shardingValues) Preconditions.checkState(!shardingValues.isEmpty(), Dynamic table should contain sharding value.); / 动态分片必须有分片值 Collection availableTargetNames = Collections.emptyList(); Collection result = new TreeSet(String.CASE_INSENSITIVE_ORDER); result.

7、addAll(doSharding(shardingValues, availableTargetNames); return result; 动态分片对应TableRule.dynamic=true 动态分片必须有分片值分片算法上。我们先看#doSharding()方法的实现。/ ShardingStrategy.java/* 计算分片* param shardingValues 分片值集合* param availableTargetNames 所有的可用分片资源集合* return 分库后指向的分片资源集合*/private Collection doSharding(final Col

8、lectionShardingValue shardingValues, final Collection availableTargetNames) / 无片键 if (shardingAlgorithm instanceof NoneKeyShardingAlgorithm) return Collections.singletonList(NoneKeyShardingAlgorithm) shardingAlgorithm).doSharding(availableTargetNames, shardingValues.iterator().next(); / 单片键 if (shar

9、dingAlgorithm instanceof SingleKeyShardingAlgorithm) SingleKeyShardingAlgorithm singleKeyShardingAlgorithm = (SingleKeyShardingAlgorithm) shardingAlgorithm; ShardingValue shardingValue = shardingValues.iterator().next(); switch (shardingValue.getType() case SINGLE: return Collections.singletonList(s

10、ingleKeyShardingAlgorithm.doEqualSharding(availableTargetNames, shardingValue); case LIST: return singleKeyShardingAlgorithm.doInSharding(availableTargetNames, shardingValue); case RANGE: return singleKeyShardingAlgorithm.doBetweenSharding(availableTargetNames, shardingValue); default: throw new Uns

11、upportedOperationException(shardingValue.getType().getClass().getName(); / 多片键 if (shardingAlgorithm instanceof MultipleKeysShardingAlgorithm) return (MultipleKeysShardingAlgorithm) shardingAlgorithm).doSharding(availableTargetNames, shardingValues); throw new UnsupportedOperationException(shardingA

12、lgorithm.getClass().getName(); 无分片键算法:对应 NoneKeyShardingAlgorithm 分片算法接口。public interface NoneKeyShardingAlgorithmT extends Comparable extends ShardingAlgorithm String doSharding(Collection availableTargetNames, ShardingValue shardingValue); 单片键算法:对应 SingleKeyShardingAlgorithm 分片算法接口。public interfac

13、e SingleKeyShardingAlgorithmT extends Comparable extends ShardingAlgorithm String doEqualSharding(Collection availableTargetNames, ShardingValue shardingValue); Collection doInSharding(Collection availableTargetNames, ShardingValue shardingValue); Collection doBetweenSharding(Collection availableTar

14、getNames, ShardingValue shardingValue);ShardingValueTypeSQL 操作符接口方法SINGLE=#doEqualSharding()LISTIN#doInSharding()RANGEBETWEEN#doBetweenSharding() 多片键算法:对应 MultipleKeysShardingAlgorithm 分片算法接口。public interface MultipleKeysShardingAlgorithm extends ShardingAlgorithm Collection doSharding(Collection av

15、ailableTargetNames, CollectionShardingValue shardingValues);分片算法类结构如下:来看看 Sharding-JDBC 实现的无需分库的分片算法 NoneDatabaseShardingAlgorithm (NoneTableShardingAlgorithm 基本一模一样):public final class NoneDatabaseShardingAlgorithm implements SingleKeyDatabaseShardingAlgorithm, MultipleKeysDatabaseShardingAlgorithm

16、 Override public Collection doSharding(final Collection availableTargetNames, final CollectionShardingValue shardingValues) return availableTargetNames; Override public String doEqualSharding(final Collection availableTargetNames, final ShardingValue shardingValue) return availableTargetNames.isEmpt

17、y() ? null : availableTargetNames.iterator().next(); Override public Collection doInSharding(final Collection availableTargetNames, final ShardingValue shardingValue) return availableTargetNames; Override public Collection doBetweenSharding(final Collection availableTargetNames, final ShardingValue

18、shardingValue) return availableTargetNames; 一定要注意,NoneXXXXShardingAlgorithm 只适用于无分库/表的需求,否则会是错误的路由结果。例如,#doEqualSharding()返回的是第一个分片资源。再来看测试目录下实现的余数基偶分表算法ModuloTableShardingAlgorithm 的实现:/ com.dangdang.ddframe.rdb.integrate.fixture.ModuloTableShardingAlgorithm.javapublic final class ModuloTableShardi

19、ngAlgorithm implements SingleKeyTableShardingAlgorithm Override public String doEqualSharding(final Collection tableNames, final ShardingValue shardingValue) for (String each : tableNames) if (each.endsWith(shardingValue.getValue() % 2 + ) return each; throw new UnsupportedOperationException(); Over

20、ride public Collection doInSharding(final Collection tableNames, final ShardingValue shardingValue) Collection result = new LinkedHashSet(tableNames.size(); for (Integer value : shardingValue.getValues() for (String tableName : tableNames) if (tableName.endsWith(value % 2 + ) result.add(tableName);

21、return result; Override public Collection doBetweenSharding(final Collection tableNames, final ShardingValue shardingValue) Collection result = new LinkedHashSet(tableNames.size(); Range range = shardingValue.getValueRange(); for (Integer i = range.lowerEndpoint(); i = range.upperEndpoint(); i+) for

22、 (String each : tableNames) if (each.endsWith(i % 2 + ) result.add(each); return result; 我们可以参考这个例子编写自己的分片算哟 多片键分库算法接口实现例子:MultipleKeysModuloDatabaseShardingAlgorithm.java动态计算分片需要怎么实现分片算法。/ com.dangdang.ddframe.rdb.integrate.fixture.SingleKeyDynamicModuloTableShardingAlgorithm.javapublic final class

23、 SingleKeyDynamicModuloTableShardingAlgorithm implements SingleKeyTableShardingAlgorithm /* * 表前缀 */ private final String tablePrefix; Override public String doEqualSharding(final Collection availableTargetNames, final ShardingValue shardingValue) return tablePrefix + shardingValue.getValue() % 10;

24、Override public Collection doInSharding(final Collection availableTargetNames, final ShardingValue shardingValue) Collection result = new LinkedHashSet(shardingValue.getValues().size(); for (Integer value : shardingValue.getValues() result.add(tablePrefix + value % 10); return result; Override publi

25、c Collection doBetweenSharding(final Collection availableTargetNames, final ShardingValue shardingValue) Collection result = new LinkedHashSet(availableTargetNames.size(); Range range = shardingValue.getValueRange(); for (Integer i = range.lowerEndpoint(); i = range.upperEndpoint(); i+) result.add(t

26、ablePrefix + i % 10); return result; 骚年,是不是明白了一些?动态表无需把真实表配置到 TableRule,而是通过分片算法计算出真实表。4. SQL 路由SQLRouter,SQL 路由器接口,共有两种实现: DatabaseHintSQLRouter:通过提示且仅路由至数据库的SQL路由器 ParsingSQLRouter:需要解析的SQL路由器它们实现#parse()进行SQL解析,#route()进行SQL路由。RoutingEngine,路由引擎接口,共有四种实现: DatabaseHintRoutingEngine:基于数据库提示的路由引擎 Si

27、mpleRoutingEngine:简单路由引擎 CartesianRoutingEngine:笛卡尔积的库表路由 ComplexRoutingEngine:混合多库表路由引擎ComplexRoutingEngine 根据路由结果会转化成 SimpleRoutingEngine 或 ComplexRoutingEngine。下文会看相应源码。路由结果有两种: RoutingResult:简单路由结果 CartesianRoutingResult:笛卡尔积路由结果从图中,我们已经能大概看到两者有什么区别,更具体的下文随源码一起分享。 SQLRouteResult:整个SQL路由返回的路由结果 R

28、outingResult:RoutingEngine返回路由结果一下子看到这么多“对象”,可能有点紧张。不要紧张,我们一起在整理下。路由器路由引擎路由结果DatabaseHintSQLRouterDatabaseHintRoutingEngineRoutingResultParsingSQLRouterSimpleRoutingEngineRoutingResultParsingSQLRouterCartesianRoutingEngineCartesianRoutingResult“对象”,是不是应该分享朋友圈。5. DatabaseHintSQLRouterDatabaseHintSQLR

29、outer,基于数据库提示的路由引擎。路由器工厂 SQLRouterFactory 创建路由器时,判断到使用数据库提示( Hint ) 时,创建 DatabaseHintSQLRouter。/ DatabaseHintRoutingEngine.javapublic static SQLRouter createSQLRouter(final ShardingContext shardingContext) return HintManagerHolder.isDatabaseShardingOnly() ? new DatabaseHintSQLRouter(shardingContext)

30、 : new ParsingSQLRouter(shardingContext);先来看下 HintManagerHolder、HintManager部分相关的代码:/ HintManagerHolder.javapublic final class HintManagerHolder /* * HintManager 线程变量 */ private static final ThreadLocal HINT_MANAGER_HOLDER = new ThreadLocal(); /* * 判断是否当前只分库. * * return 是否当前只分库. */ public static bool

31、ean isDatabaseShardingOnly() return null != HINT_MANAGER_HOLDER.get() & HINT_MANAGER_HOLDER.get().isDatabaseShardingOnly(); /* * 清理线索分片管理器的本地线程持有者. */ public static void clear() HINT_MANAGER_HOLDER.remove(); / HintManager.javapublic final class HintManager implements AutoCloseable /* * 库分片值集合 */ pri

32、vate final MapShardingKey, ShardingValue databaseShardingValues = new HashMap(); /* * 只做库分片 * link DatabaseHintRoutingEngine */ Getter private boolean databaseShardingOnly; /* * 获取线索分片管理器实例. * * return 线索分片管理器实例 */ public static HintManager getInstance() HintManager result = new HintManager(); HintM

33、anagerHolder.setHintManager(result); return result; /* * 设置分库分片值. * * 分片操作符为等号.该方法适用于只分库的场景 * * param value 分片值 */ public void setDatabaseShardingValue(final Comparable value) databaseShardingOnly = true; addDatabaseShardingValue(HintManagerHolder.DB_TABLE_NAME, HintManagerHolder.DB_COLUMN_NAME, val

34、ue); 那么如果要使用 DatabaseHintSQLRouter,我们只需要HintManager.getInstance().setDatabaseShardingValue(库分片值)即可。这里有两点要注意下: HintManager#getInstance(),每次获取到的都是新的 HintManager,多次赋值需要小心。 HintManager#close(),使用完需要去清理,避免下个请求读到遗漏的线程变量。看看 DatabaseHintSQLRouter 的实现:/ DatabaseHintSQLRouter.javaOverridepublic SQLStatement p

35、arse(final String logicSQL, final int parametersSize) return new SQLJudgeEngine(logicSQL).judge(); / 只解析 SQL 类型 Override/ TODO insert的SQL仍然需要解析自增主键public SQLRouteResult route(final String logicSQL, final List parameters, final SQLStatement sqlStatement) Context context = MetricsContext.start(Route S

36、QL); SQLRouteResult result = new SQLRouteResult(sqlStatement); / 路由 RoutingResult routingResult = new DatabaseHintRoutingEngine(shardingRule.getDataSourceRule(), shardingRule.getDatabaseShardingStrategy(), sqlStatement.getType() .route(); / SQL最小执行单元 for (TableUnit each : routingResult.getTableUnits

37、().getTableUnits() result.getExecutionUnits().add(new SQLExecutionUnit(each.getDataSourceName(), logicSQL); MetricsContext.stop(context); if (showSQL) SQLLogger.logSQL(logicSQL, sqlStatement, result.getExecutionUnits(), parameters); return result; #parse()只解析了 SQL 类型,即 SELECT / UPDATE / DELETE / INS

38、ERT 。 使用的分库策略来自 ShardingRule,不是 TableRule,这个一定要留心。因为 SQL 未解析表名。因此,即使在 TableRule 设置了actualTables属性也是没有效果的。 目前不支持 Sharding-JDBC 的主键自增。因为 SQL 未解析自增主键。从代码上的TODO应该会支持。 HintManager.getInstance().setDatabaseShardingValue(库分片值)设置的库分片值使用的是 EQUALS,因而分库策略计算出来的只有一个库分片,即 TableUnit 只有一个,SQLExecutionUnit 只有一个。看看 D

39、atabaseHintSQLRouter 的实现:/ DatabaseHintRoutingEngine.javaOverridepublic RoutingResult route() / 从 Hint 获得 分片键值 OptionalShardingValue shardingValue = HintManagerHolder.getDatabaseShardingValue(new ShardingKey(HintManagerHolder.DB_TABLE_NAME, HintManagerHolder.DB_COLUMN_NAME); Preconditions.checkState

40、(shardingValue.isPresent(); log.debug(Before database sharding only db: sharding values: , dataSourceRule.getDataSourceNames(), shardingValue.get(); / 路由。表分片规则使用的是 ShardingRule 里的。因为没 SQL 解析。 Collection routingDataSources = databaseShardingStrategy.doStaticSharding(sqlType, dataSourceRule.getDataSou

41、rceNames(), Collections.ShardingValuesingleton(shardingValue.get(); Preconditions.checkState(!routingDataSources.isEmpty(), no database route info); log.debug(After database sharding only result: , routingDataSources); / 路由结果 RoutingResult result = new RoutingResult(); for (String each : routingDataSources) result.getTableUnits().getTableUnits().add(new TableUnit(each, , );

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 社会民生


经营许可证编号:宁ICP备18001539号-1