盘点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 配置

<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>shardingsphere-jdbc-core</artifactId>
    <version>5.0.0-alpha</version>
</dependency>

复制代码

Config 配置

@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;
    }
}

复制代码

实体类

@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

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

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 多数据源的加载

// 核心的配置加载来自于 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.


// 此处为 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 问题 , 但是直觉告诉我 , 这还是配置出现了偏差

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

// 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 类去反推就大概知道了


// 配置映射
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 发起执行
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 的主要处理逻辑

// 该类中只有一个方法
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

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 中缺少相关的策略 , 配置文件写法问题 , 补齐后 , 一切正常

最终配置文件

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 即为一个数据库处理对象 , 会在对应的数据库/数据表中执行

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

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享