盘点Sharding-JDBC 分库分表

总文档 :文章目录

Github : github.com/black-ant

一 . 前言

本来没准备看这部分源码的, 事情的起因是因为之前一直在使用 3.0 的版本 ,这次看到5.0了想试试 ,结果没有跑起来, 官方文档又不给力 ,索性把源码看了一遍 , 把问题找到了…..

二 . 事情的起点

起因是因为使用 shardingsphere-jdbc-core-spring-boot-starter 依赖 , 想直接通过配置文件完成所有的配置 , 但是整个流程出现了不少问题 .

幸运的是之前写过一个 Bean 配置的 , 正好可以对比看看整个流程的问题.

2.1 Java Bean 配置方式

这里先将 Java Bean 配置的整个流程贴上来 , 用于对比 .2者都是使用 JPA 作为持久化框架

Maven 配置

1
2
3
4
5
xml复制代码<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core</artifactId>
<version>5.0.0-alpha</version>
</dependency>

Config 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
java复制代码@Configuration
public class DatabaseConfig {

/**
* 方式一 : 通过 Bean 配置
*/
@Bean
public DataSource dataSource() {
// 配置真实数据源
Map<String, DataSource> dataSourceMap = new HashMap<>();

// 配置第 1 个数据源
BasicDataSource dataSource1 = new BasicDataSource();
dataSource1.setDriverClassName("com.mysql.jdbc.Driver");
dataSource1.setUrl("jdbc:mysql://127.0.0.1:3306/database0?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&serverTimezone=UTC");
dataSource1.setUsername("root");
dataSource1.setPassword("123456");
dataSourceMap.put("ds0", dataSource1);

// 配置第 2 个数据源
BasicDataSource dataSource2 = new BasicDataSource();
dataSource2.setDriverClassName("com.mysql.jdbc.Driver");
dataSource2.setUrl("jdbc:mysql://127.0.0.1:3306/database1?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&serverTimezone=UTC");
dataSource2.setUsername("root");
dataSource2.setPassword("123456");
dataSourceMap.put("ds1", dataSource2);

// 配置 t_order 表规则
ShardingTableRuleConfiguration orderTableRuleConfig = new ShardingTableRuleConfiguration("t_blog", "ds${0..1}.t_blog_${0..1}");

// 配置主键生成策略
KeyGenerateStrategyConfiguration configuration = new KeyGenerateStrategyConfiguration("id", null);
orderTableRuleConfig.setKeyGenerateStrategy(configuration);

// 配置分库策略
orderTableRuleConfig.setDatabaseShardingStrategy(new StandardShardingStrategyConfiguration("column_id", "dbShardingAlgorithm"));

// 配置分表策略
orderTableRuleConfig.setTableShardingStrategy(new StandardShardingStrategyConfiguration("title_id", "tableShardingAlgorithm"));

// 配置分片规则
ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
shardingRuleConfig.getTables().add(orderTableRuleConfig);

// 配置分库算法
Properties dbShardingAlgorithmrProps = new Properties();
dbShardingAlgorithmrProps.setProperty("algorithm-expression", "ds${column_id % 2}");
shardingRuleConfig.getShardingAlgorithms().put("dbShardingAlgorithm", new ShardingSphereAlgorithmConfiguration("INLINE", dbShardingAlgorithmrProps));

// 配置分表算法
Properties tableShardingAlgorithmrProps = new Properties();
tableShardingAlgorithmrProps.setProperty("algorithm-expression", "t_blog_${title_id % 2}");
shardingRuleConfig.getShardingAlgorithms().put("tableShardingAlgorithm", new ShardingSphereAlgorithmConfiguration("INLINE", tableShardingAlgorithmrProps));

DataSource dataSource = null;
try {
dataSource = ShardingSphereDataSourceFactory.createDataSource(dataSourceMap, Collections.singleton(shardingRuleConfig), new Properties());
} catch (SQLException e) {
e.printStackTrace();
}
//logger.info("datasource : {}", dataSource);
return dataSource;
}
}

实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
java复制代码@Entity
@Table(name = "t_blog")
public class BlogEntity {

@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = "title")
private String title;

@Column(name = "title_id")
private Integer titleId;

@Column(name = "author")
private String author;

@Column(name = "date")
private Date date;

@Column(name = "column_id")
private Integer columnId;

//.......................
}

三 . Properties 配置方式

为了避免误导 , 配置文件在最后贴 ,我们从异常开始看起

java.util.NoSuchElementException: No value bound

这是第一个出现的异常 , 看异常栈里面 , 有这样几个重要的提示 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
java复制代码Caused by: java.lang.reflect.InvocationTargetException: null
at org.apache.shardingsphere.spring.boot.util.PropertyUtil.v2(PropertyUtil.java:111) ~[shardingsphere-jdbc-spring-boot-starter-infra-5.0.0-alpha.jar:5.0.0-alpha]
at org.apache.shardingsphere.spring.boot.util.PropertyUtil.handle(PropertyUtil.java:75) ~[shardingsphere-jdbc-spring-boot-starter-infra-5.0.0-alpha.jar:5.0.0-alpha]
at org.apache.shardingsphere.spring.boot.datasource.DataSourceMapSetter.getDataSourceMap(DataSourceMapSetter.java:66) ~[shardingsphere-jdbc-spring-boot-starter-infra-5.0.0-alpha.jar:5.0.0-alpha]

// 可以看到 , 出问题的地方为 org.apache.shardingsphere.spring.boot.util.PropertyUtil.v2

// 往上2步 , 可以跟踪到节点 : getDataSourceMap
// 这里就是我们第一个要看的地方 : 多数据源的加载


// 先看下异常的原因 , 再看看整体逻辑
public static Map<String, DataSource> getDataSourceMap(final Environment environment) {
Map<String, DataSource> result = new LinkedHashMap<>();
Map<String, Object> dataSourceCommonProps = PropertyUtil.handle(environment, COMMON_PREFIX, Map.class);

//...

}


// 基本上可以按出来 , 这里缺少 COMMON_PREFIX 打头的配置项 , 查了相关资料 ,大概是这2句
spring.shardingsphere.datasource.common.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.common.driver-class-name=com.mysql.jdbc.Driver

// 问题解决 , 看看整体逻辑

3.1 多数据源

3.1.1 多数据源的加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
java复制代码// 核心的配置加载来自于 org.apache.shardingsphere.spring.boot 包下 , 配置了如下
C10- SpringBootConfiguration
F01- SpringBootPropertiesConfiguration : 该类种只有一个 Properties 属性
F02- Map<String, DataSource> dataSourceMap : 维护了一个 DataSource 的列表
M10_01- shardingSphereDataSource
- ShardingSphereDataSourceFactory.createDataSource : 传入 Map 集合创建 DataSource -> M11_01
M10_02- ShardingTransactionTypeScanner : 事务相关

C11- ShardingSphereDataSourceFactory : Datasource 的工厂类
M11_01- createDataSource(Map<String, DataSource>,Collection<RuleConfiguration>, Properties)
?- 创建一个 DataSource , 可以看到 , 这里传入的是一个 DataSource 的集合 , 盲猜原理是创建一个虚拟的DataSource
?- Map<String, DataSource> -> PS_M11_01_1
- new ShardingSphereDataSource(dataSourceMap, configurations, props) -> C12MC

// M11_01 代码
public static DataSource createDataSource(final DataSource dataSource, final Collection<RuleConfiguration> configurations, final Properties props) throws SQLException {
Map<String, DataSource> dataSourceMap = new HashMap<>(1, 1);
dataSourceMap.put(DefaultSchema.LOGIC_NAME, dataSource);
return createDataSource(dataSourceMap, configurations, props);
}


// 由 M11_01 中构建相关对象
C12- ShardingSphereDataSource
MC- ShardingSphereDataSource
- 生成一个 DatabaseType -> M12_01
- 构建一个 SchemaContextsBuilder
- 构建一个 TransactionContexts
M12_01- createDatabaseType(final Map<String, DataSource> dataSourceMap)
FOR- 对 dataSourceMap 循环 , 分别调用 createDatabaseType -> M12_02
- 返回的是最后一个 DataSource 的 DatabaseType
?- 这里的 FOR 循环目的是什么 ? 既然返回的一定是最后一个 , 那循环那么多干嘛
M12_02- createDatabaseType(final DataSource dataSource)
?- 注意 , 这里是2个参数不同的方法
- dataSource.getConnection() : 获取 Connect
- 生成 DatabaseType

// M12_02伪代码 : 通过 DatabaseTypeRegistry 获取 DatabaseType , 此处拿到的是 MySQLDatabaseType
private DatabaseType createDatabaseType(final DataSource dataSource) throws SQLException {
if (dataSource instanceof ShardingSphereDataSource) {
return ((ShardingSphereDataSource) dataSource).schemaContexts.getDatabaseType();
}
try (Connection connection = dataSource.getConnection()) {
return DatabaseTypeRegistry.getDatabaseTypeByURL(connection.getMetaData().getURL());
}
}

PS_M11_01_1 : 传入的资源依赖于 spring.shardingsphere.datasource.names 属性

image-20210427141008081.png

可以看到 , 尽管我的配置中配置了 3 个 datasource , 但是最终使用的依赖于属性 spring.shardingsphere.datasource.names.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
java复制代码
// 此处为 dataSourceMap , 用于扫描配置中的数据源 ,起点逻辑为 :

// PS : 该方法为 EnvironmentAware 接口的实现方法 , 会在ApplicationContextAwareProcessor 中默认调用
public final void setEnvironment(final Environment environment) {
dataSourceMap.putAll(DataSourceMapSetter.getDataSourceMap(environment));
}


// 扫描具体的 DataSource
C- DataSourceMapSetter
F- private static final String PREFIX = "spring.shardingsphere.datasource.";
F- private static final String COMMON_PREFIX = "spring.shardingsphere.datasource.common.";
M1- getDataSourceMap
- 获取 Common 属性 : spring.shardingsphere.datasource.common.
- 获取 Srouce 名称集合 -> M2
- 依次生成 DataSource 放入集合 -> M3
M2- getDataSourceNames : 获取 datasource Name , 名称为拼接出来的
- 优先获取 spring.shardingsphere.datasource.name
- 没有则获取 spring.shardingsphere.datasource.names
M3- getDataSource
- Map<String, Object> dataSourceProps = mergedDataSourceProps(...) : 生成属性
- Preconditions.checkState(...) : 校验参数是否合法
- DataSourceUtil.getDataSource(...) : 创建 DataSource
- 后面是一个语法糖 , 用于注入属性 -> PS_M3_001

// PS_M3_001 : 最终执行语句就是 HikariDataSourcePropertiesSetter # propertiesSet
// 会获取 Environment 中 spring.shardingsphere.datasource.ds1.data-source-properties 前缀 , 设置相关属性
DataSourcePropertiesSetterHolder.getDataSourcePropertiesSetterByType(dataSourceProps.get(DATA_SOURCE_TYPE).toString()).ifPresent(propsSetter -> propsSetter.propertiesSet(environment, prefix, dataSourceName, result));

3.1.2 多数据源总结

起点 : SpringBootConfiguration

处理核心 : ShardingSphereDataSourceFactory#createDataSource

构建单元 : ShardingSphereDataSource

扫描类 : DataSourceMapSetter

一句话总结 :

  • SpringBootConfiguration 实现了EnvironmentAware 方法 , 则在容器加载的同时 , 会默认调用 SpringBootConfiguration 中 setEnvironment 方法
  • setEnvironment 方法会扫描配置文件中的 Datasourse 配置 ,放入 一个集合 dataSourceMap
  • SpringBootConfiguration 在使用 shardingSphereDataSource 加载多数据源时 , 使用该 Map
  • shardingSphereDataSource 方法调用 ShardingSphereDataSourceFactory 创建数据源
  • ShardingSphereDataSourceFactory 最终会构建出 ShardingSphereDataSource , 标识一个数据源

3.2 分库分表策略配置

配置跑通后 , 又出现了问题 :

java.sql.SQLException: Parameter index out of range (1 > number of parameters, which is 0).

运行时抛出如上异常 , 虽然说的时 SQL 问题 , 但是直觉告诉我 , 这还是配置出现了偏差

于是按照如下步骤进行排查 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
java复制代码// Step 1 : PreparedStatement  # checkBounds
// 异常栈第一步就是这里 , 发现校验出现问题 , 抛出了异常
if ((paramIndex < 1)) {
throw SQLError.createSQLException(........);
} else if (paramIndex > this.parameterCount) {
throw SQLError.createSQLException(.......);
}........


// Step 2 : 排查 PreparedStatement 初始化方法
C- PreparedStatement
M-initializeFromParseInfo()
- 观察 staticSqlStrings 发现 , 参数是错误的 , 只有一个数组
insert into org.apache.shardingsphere.sharding.rewrite.token.pojo.TableToken@3df8c40 (author, column_id, date, title, title_id, id) values org.apache.shardingsphere.sharding.rewrite.token.pojo.ShardingInsertValuesToken@c1b5afe
// 此处发现这个值也是错误的 , 猜测应该是 分表时 , table 处理出现异常


// Step 3 : 排查分表的逻辑
C79- ShardingRouteEngine
M79_05- route0
- routeDataSources 获取策略判断的 DataSource 节点
- routeTables 获取判断的 table 表

// StandardShardingStrategy # doSharding
target = shardingAlgorithm.doSharding(availableTargetNames, new PreciseShardingValue(shardingValue.getTableName(), shardingValue.getColumnName(), each));

// 这里 debug 发现 shardingValue.getTableName() 名称不对 -------> dt_blog_1 , 应该是t_blog_1
// 我的天.....这一看就是配置问题了 , 检查一下
spring.shardingsphere.rules.sharding.sharding-algorithms.db-algorithm.props.algorithm-expression=dt_blog_$->{title_id % 2}

// 改为
spring.shardingsphere.rules.sharding.sharding-algorithms.db-algorithm.props.algorithm-expression=t_blog_$->{title_id % 2}

// 一个通过源码排查异常的流程旧结束了 , 重新

这其实就是一个配置问题 , 但是 Sharding 返回的异常信息是看不出来原因的 .

总结

主要注意其中 C79- ShardingRouteEngine / M79_05- route0 方法 , 改方法中有2个主要的方法 , 分库分表的核心判断就在该方法中

这里把策略的加载路径记录一下:

虽然是 properties ,但是使用的还是 yml 的加载类 , 不冲突

具体却什么配置 ,按照 YamlShardingRuleConfiguration 类去反推就大概知道了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
java复制代码
// 配置映射
C- YamlShardingStrategyConfiguration
?- 数据配置在上一步被配置到当前的 Configuration , 可以通过该类中的属性进行直接配置

// Rule 规则
C- ShardingRuleSpringBootConfiguration

C- ShardingRuleAlgorithmProviderConfigurationYamlSwapper

C- ShardingTableRuleConfigurationYamlSwapper

// algorithm-expression 的注入位置
C- ShardingRule
M- ShardingRule(final AlgorithmProvidedShardingRuleConfiguration config, final Collection<String> dataSourceNames)

3.3 分库分表逻辑

很好 , 项目这里总算跑起来了 , 但是分库分表的功能并没有实现 , 运行时 , 四个表均创建了

Bebug 发现 , ShardingSpherePreparedStatement 的 result 中返回了四个数据

PS:M74_02_01

sharding-jdbc-executionContext.jpg

于是按照以下流程过了一遍分库分表的流程 :

可以看到 , 第一个入口类是 ShardingSpherePreparedStatement

Step 1 : ShardingSpherePreparedStatement

该类是一个入口类 , 相关的操作均可以从该类向下查找 :

  • 核心一 : M74_01中获取的StatementExecuteUnit即为最终会执行的SQL , 这其中已经包含了需要运行的库(详见上图) , 所以 createExecutionContext 时 , 相关数据已经生成
  • 核心二 : M74_01中preparedStatementExecutor.executeUpdate 发起执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
java复制代码C74- ShardingSpherePreparedStatement
M74_01- executeUpdate() : 执行插入操作
?- 这个里面有3个核心操作 , 获取需要执行的 statement + update 执行语句
- createExecutionContext 创建一个 ExecutionContext -> M74_03
?- 核心逻辑 , 生成需要执行的对象 ExecuteUnit
- getInputGroups 获取一个 StatementExecuteUnit 集合 -> M74_02
- preparedStatementExecutor.executeUpdate 执行当前语句
M74_02- getInputGroups()
?- 核心逻辑就是通过相关的 rule 规则生成对应的执行语句
- 构建一个 PreparedStatementExecuteGroupEngine
- 调用 PreparedStatementExecuteGroupEngine generate 方法获取数据
?- 参数一 : executionContext.getRouteContext()
?- 参数二 : executionContext.getExecutionUnits() -> PS:M74_02_01
M74_03- createExecutionContext()
- kernelProcessor.generateExecutionContext 生成 ExecutionContext -> M75_01
- findGeneratedKey 创建主键


// M74_02 代码
private Collection<InputGroup<StatementExecuteUnit>> getInputGroups() throws SQLException {
// 最大连接数
int maxConnectionsSizePerQuery = schemaContexts.getProps().<Integer>getValue(ConfigurationPropertyKey.MAX_CONNECTIONS_SIZE_PER_QUERY);
//
return new PreparedStatementExecuteGroupEngine(maxConnectionsSizePerQuery, connection, statementOption,
schemaContexts.getDefaultSchema().getRules()).generate(executionContext.getRouteContext(), executionContext.getExecutionUnits());
}

知道了源头在 M74_01 中 , 继续向下找 -> M75_01

Step 2 : Rule 的主要处理逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
java复制代码// 该类中只有一个方法
C75- KernelProcessor
M75_01- generateExecutionContext
1- 获取 ShardingSphereRule 集合
2- 创建一个 SQLRouteEngine
3- 获取一个 SQLStatementContext : 这里可以映射为一个数据库操作
4- 获取 RouteContext : 这个对象决定将会执行几个数据库/数据表 -> M76_01

// M75_01
public ExecutionContext generateExecutionContext(final LogicSQL logicSQL, final ShardingSphereSchema schema, final ConfigurationProperties props) {
// PS:M75_01_01 rule 规则对象
Collection<ShardingSphereRule> rules = schema.getRules();
SQLRouteEngine sqlRouteEngine = new SQLRouteEngine(rules, props);
SQLStatementContext<?> sqlStatementContext = logicSQL.getSqlStatementContext();

// 核心语句 : 获取 RouteContext : 这个对象决定将会执行几个数据库/数据表 -> M76_01
RouteContext routeContext = sqlRouteEngine.route(logicSQL, schema);
SQLRewriteEntry rewriteEntry = new SQLRewriteEntry(schema.getMetaData().getSchemaMetaData().getConfiguredSchemaMetaData(), props, rules);
SQLRewriteResult rewriteResult = rewriteEntry.rewrite(logicSQL.getSql(), logicSQL.getParameters(), sqlStatementContext, routeContext);
Collection<ExecutionUnit> executionUnits = ExecutionContextBuilder.build(schema.getMetaData(), rewriteResult, sqlStatementContext);
return new ExecutionContext(sqlStatementContext, executionUnits, routeContext);
}

PS:M75_01_01 Rule 结构

可以看到 , 包括多数据源 , 分片规则 ,都已经在里面了

image.png

剩下的就简单了 , 找到之前处理的地方就行了

Step 3 : 一步步 Debug

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
java复制代码C76- SQLRouteEngine
M76_01- route :
- 创建一个 SQLRouteExecutor
?- 参数 : new PartialSQLRouteExecutor(rules, props) , 这里就将所有的 Rule 注入到体系中了
- executor.route 调用执行器执行 rule -> M77_01


C77- PartialSQLRouteExecutor
M77_01- route : 终于找到了 , 这里通过 Rule 规则生成相关的 SQLRouter
- entry.getValue().createRouteContext(logicSQL, schema, entry.getKey(), props) : 核心语句 -> M78_01

C78- ShardingSQLRouter
M78_01- createRouteContext
- ShardingRouteEngineFactory 构建一个 RouteEngine 并且调用对应 route 方法 -> M79_01

C79- ShardingRouteEngine
M79_01- route(RouteContext routeContext, ShardingRule shardingRule)
- getDataNodes 获取 Collection<DataNode> : -> M79_03
?- 此处的 Node 会用于下面生成 RouteUnit -> PS:M79_01_02
FOR- 循环获取的 Nodes , 创建 RouteUnit
M79_02- getDataNodes
- createShardingStrategy 创建策略
M79_03- routeByShardingConditions
?- 这里会通过 Condition 不同分别调用2个方法
- route0 -> M79_05
- routeByShardingConditionsWithCondition -> M79_04
M79_04- routeByShardingConditionsWithCondition : 核心逻辑1
For- 循环所有的 Condition , 获取策略和 ShardingValues 值 , 调用 route0
M79_05- route0
- routeDataSources 获取策略判断的 DataSource 节点
- routeTables 获取判断的 table 表
M79_06- routeTables
- 这里就会调用对应的 Strategy 策略类

// PS : 如果这里发现 TableRule 存在问题 , 可以查看方法 是否存在问题
private Collection<DataNode> getDataNodes(final ShardingRule shardingRule, final TableRule tableRule) {
ShardingStrategy databaseShardingStrategy = createShardingStrategy(shardingRule.getDatabaseShardingStrategyConfiguration(tableRule), shardingRule.getShardingAlgorithms());
ShardingStrategy tableShardingStrategy = createShardingStrategy(shardingRule.getTableShardingStrategyConfiguration(tableRule), shardingRule.getShardingAlgorithms());
if (isRoutingByHint(shardingRule, tableRule)) {
return routeByHint(tableRule, databaseShardingStrategy, tableShardingStrategy);
}
if (isRoutingByShardingConditions(shardingRule, tableRule)) {
return routeByShardingConditions(shardingRule, tableRule, databaseShardingStrategy, tableShardingStrategy);
}
return routeByMixedConditions(shardingRule, tableRule, databaseShardingStrategy, tableShardingStrategy);
}


// M79_03 源代码
private Collection<DataNode> routeByShardingConditions(final ShardingRule shardingRule, final TableRule tableRule,
final ShardingStrategy databaseShardingStrategy, final ShardingStrategy tableShardingStrategy) {
return shardingConditions.getConditions().isEmpty()
? route0(tableRule, databaseShardingStrategy, Collections.emptyList(), tableShardingStrategy, Collections.emptyList())
: routeByShardingConditionsWithCondition(shardingRule, tableRule, databaseShardingStrategy, tableShardingStrategy);
}


C80- StandardShardingStrategy
M80_01- doSharding

这里也不详细说了 , 一步步debug 下来 , 发现是 M79_04- routeByShardingConditionsWithCondition 中缺少相关的策略 , 配置文件写法问题 , 补齐后 , 一切正常

最终配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
properties复制代码server.port=8085
##Jpa配置
spring.jpa.database=mysql
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
# 开启配置
spring.shardingsphere.enabled=true
# 配置真实数据源 ds0,ds1,ds2
spring.shardingsphere.datasource.names=ds0,ds1
spring.shardingsphere.datasource.common.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.common.driver-class-name=com.mysql.jdbc.Driver
# 配置第 1 个数据源
spring.shardingsphere.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds0.jdbc-url=jdbc:mysql://127.0.0.1:3306/database0?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&serverTimezone=UTC
spring.shardingsphere.datasource.ds0.username=root
spring.shardingsphere.datasource.ds0.password=123456
# 配置第 2 个数据源
spring.shardingsphere.datasource.ds1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds1.jdbc-url=jdbc:mysql://127.0.0.1:3306/database1?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&serverTimezone=UTC
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=123456
# 配置表策略 -- ShardingTableRuleConfiguration
spring.shardingsphere.rules.sharding.tables.t_blog.actual-data-nodes=ds$->{0..1}.t_blog_$->{0..1}
# 配置主键策略 -- KeyGenerateStrategyConfiguration
spring.shardingsphere.rules.sharding.tables.t_blog.key-generate-strategy.column=id
spring.shardingsphere.rules.sharding.tables.t_blog.key-generate-strategy.key-generator-name=snowflake
# 配置分表策略 StandardShardingStrategyConfiguration
spring.shardingsphere.rules.sharding.tables.t_blog.binding-tables=t_blog
spring.shardingsphere.rules.sharding.tables.t_blog.table-strategy.standard.sharding-column=title_id
spring.shardingsphere.rules.sharding.tables.t_blog.table-strategy.standard.sharding-algorithm-name=db-algorithm
# 配置分库策略 StandardShardingStrategyConfiguration
spring.shardingsphere.rules.sharding.tables.t_blog.database-strategy.standard.sharding-column=column_id
spring.shardingsphere.rules.sharding.tables.t_blog.database-strategy.standard.sharding-algorithm-name=table-algorithm
# ================================
# =====================配置默认策略
# 默认插入类型
spring.shardingsphere.rules.sharding.default-database-strategy.standard.sharding-column=id
spring.shardingsphere.rules.sharding.default-database-strategy.standard.sharding-algorithm-name=database_inline
# 指定 algorithms
spring.shardingsphere.rules.sharding.sharding-algorithms.database-inline.type=INLINE
spring.shardingsphere.rules.sharding.sharding-algorithms.database-inline.props.algorithm-expression=ds$->{id % 2}
# 默认算法
spring.shardingsphere.rules.sharding.key-generators.snowflake.type=SNOWFLAKE
spring.shardingsphere.rules.sharding.key-generators.snowflake.props.worker-id=123
# 切分策略
spring.shardingsphere.rules.sharding.sharding-algorithms.db-algorithm.type=INLINE
spring.shardingsphere.rules.sharding.sharding-algorithms.db-algorithm.props.algorithm-expression=t_blog_$->{title_id % 2}
spring.shardingsphere.rules.sharding.sharding-algorithms.table-algorithm.type=INLINE
spring.shardingsphere.rules.sharding.sharding-algorithms.table-algorithm.props.algorithm-expression=ds$->{column_id % 2}


# !!!!! 对应表 t_blog_0 , t_blog_1 , 已经上面2个库

总结

Sharding-JDBC 分析中最麻烦的就是 lombok 表达式了 , debug 起来叫一个麻烦 , 还要一个个去配置 , 想想之前改掉了这个习惯还是比较庆幸的 .

整个过程中主要有3个节点 :

  • SpringBootConfiguration
  • ShardingRouteEngine
  • StandardShardingStrategy

头尾中间都在这里了 , 以这三个节点去debug ,基本上问题就出来了

其中需要明白的是 : sharding-jdbc 会在策略执行完成后 , 生成多个 ExecuteUnit , 每一个 ExecuteUnit 即为一个数据库处理对象 , 会在对应的数据库/数据表中执行

后续再来看看他的分布式事务和读写分离的主要逻辑 , 这里先记这么多

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%