JavaAPI 的结构

(sharding-jdbc的版本为 5.3.1)

Sharding-JDBC 的 JavaAPI 的结构如下图所示,整体上,分为两大块:

  • rule: 表的基本配置,如逻辑表名,物理节点等.
  • stategy: 库和表的分片策略、主键生成、审计等策略的配置.

2023-03-10T14:30:29.png

所以在具体的配置时,也是按照这个框架进配置,具体的关系我再画了张图,便于理解:
由于在我的项目里没有用到审计,所以审计(audit)相关的暂时不作任何配置.

2023-03-10T14:57:02.png

从上图可以看出,在整个配置里,所有的算法和表规则是分开的,在表规则里用到的算法,在算法列表里必须存在.

分片策略

在 ShardingSphere 的官方文档里,将分片算法 + 分片键称为分片策略. 一共有四种策略(sharding-jdbc 5.3.1版本):

  • Standard 标准分片策略:用于只有一个分片键的场景.
  • Complex 复合分片策略:用于有多个分片键的场景.
  • Hint 分片策略:Hint的意思是暗示,也就是用于没有明确指定分片键的场景,即从SQL之外的逻辑来决定如何分片.
  • None 分片策略:用于配置不分片的策略.

相对应的配置类如下,它们都继承自 ShardingStrategyConfiguration.

2023-03-10T15:04:55.png

Standard, None, Hit, Complex

算法配置

算法配置的类为 AlgorithmConfiguration,从类的定义可以看出,要配置一个算法,需要指定以下两个属性的值:

  • type: 算法的类型
  • props: 算法对应的属性列表

算法共分为五类(具体的type和props的说明可以参考官方文档说明):

  • 自动分片算法: 适用于一些简答的场景,比如取模、容量范围、边界等.
  • 标准分片算法: 包括行表达式 INLINE,时间范围分片算法等.
  • 复合分片算法: 包括复合行表达式 COMPLEX_INLINE 等.
  • Hint 分片算法: 包括Hint行表达式分片算法 HINT_INLINE 等.
  • 自定义类分片算法: 通过配置分片策略类型和算法类名,实现自定义扩展, 比较常用.(type为:CLASS_BASED)

这里着重强调下,因为后面会用到,自定义类分片算法有两个属性配置

  • strategy:指定策略,比如你想自定义单分片键,那就选择 STANDARD,如果多分片键,那就选择 COMPLEX.
  • algorithmClassName: 自定义的算法实现类的全限定名,可以通过你的实现类的 class 的 getName 方法获取,比如:MyAlgorithm.class.getName().

JavaAPI具体配置方法

在上面介绍了一些基本概念知识后,我们可以总结出配置的顺序:

  • 1 配置数据库
  • 2 配置所用到的所有算法到算法列表中
  • 3 配置表的基本信息:逻辑表名,物理节点等
  • 4 配置表的分库策略、分表策略、主键生成策略
  • 5 根据规则配置初始化数据源 dataSource.

配置数据库

我测试的demo中配置了4个数据库,逻辑库名模板为:ds_%d, 实际库名为:demo_ds_%d
具体配置文件中的信息如下:

sharding:
  datasource:
    total: 4
    nameTemplate: ds_%d
    dataSourceClassName: com.zaxxer.hikari.HikariDataSource
    driverClassName: com.mysql.cj.jdbc.Driver
    jdbcUrl: jdbc:mysql://localhost:3306/demo_ds_%d?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8&allowPublicKeyRetrieval=true
    username: root
    password: 123456

Java代码中需要循环4次,为每一个数据库初始化连接池,并放入 map 中,key 为逻辑库名 ds_%d.

// 配置数据库
Map<String, DataSource> dataSourceMap = new HashMap<>();

for (int i = 0; i < shardingProperties.getTotal(); i++) {
    String dbName = String.format(shardingProperties.getNameTemplate(), i);
    DataSource dataSource = hikariDataSource(i);
    dataSourceMap.put(dbName, dataSource);
}

配置算法列表

分片使用标准算法:行表达式 INLINE
主键生成使用内置雪花算法:SNOWFLAKE

ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();

// 1 - 算法配置
// 分库算法
Properties dsShardingProps = new Properties();
dsShardingProps.setProperty("algorithm-expression", "ds_${user_id % 4}");
shardingRuleConfig.getShardingAlgorithms().put("database_inline", new AlgorithmConfiguration("INLINE", dsShardingProps));

// 分表算法
Properties tableShardingProps = new Properties();
tableShardingProps.setProperty("algorithm-expression", "t_order_${order_id % 6}");
shardingRuleConfig.getShardingAlgorithms().put("table_inline", new AlgorithmConfiguration("INLINE", tableShardingProps));

// 主键生成算法
shardingRuleConfig.getKeyGenerators().put("snow_flake", new AlgorithmConfiguration("SNOWFLAKE", new Properties()));

自定义算法可以这样配置
首先按照实际需求实现一个算法接口,我这里选择 ComplexKeysShardingAlgorithm 接口,
需要实现下面的三个方法,最核心的是 doSharding 方法,有两个比较重要的参数:

  • availableTargetNames:所有可用的目标名称,如果你自定义的这个算法用于分库,就是库名列表,如果用于分表,就是表名列表.
  • shardingValue 包装了所有的分片键的信息.

你要在这个方法里做的事情就是:

根据 shardingValue,通过自定义的路由逻辑,最终从 availableTargetNames 中筛选出实际需要的 names.

我这个例子中,路由逻辑比较简单,就是取余.

public class DataSourceShardingAlgorithms implements ComplexKeysShardingAlgorithm<Long> {

    private Properties props;

    @Override
    public Properties getProps() {
        return props;
    }

    @Override
    public void init(final Properties props) {
        this.props = props;
    }

    @Override
    public Collection<String> doSharding(final Collection<String> availableTargetNames, final ComplexKeysShardingValue<Long> shardingValue) {

        Set<String> result = new HashSet<>();

        // 根据 分片健 和 对应的值,来确定目标数据库
        // 比如目前 ds_demo_0, ds_demo_1, 算法为 取余
        Map<String, Collection<Long>> map = shardingValue.getColumnNameAndShardingValuesMap();

        if (!CollectionUtils.isEmpty(map)) {

            for (String key : map.keySet()) {
                Collection<Long> values = map.get(key);
                for (Long value : values) {
                    String targetSuffix =  String.valueOf(value % availableTargetNames.size());

                    for (String targetName : availableTargetNames) {
                        if (targetName.endsWith(targetSuffix)) {
                            result.add(targetName);
                        }
                    }
                }
            }
        }

        return result;
    }
}

在创建好自定的算法后,按照下面的方式将算法添加到算法列表中:

Properties dsShardingProps = new Properties();
dsShardingProps.setProperty("strategy", "COMPLEX");
dsShardingProps.setProperty("algorithmClassName", DataSourceShardingAlgorithms.class.getName());

shardingRuleConfig.getShardingAlgorithms().put("MY_ALGORITHM", new AlgorithmConfiguration("CLASS_BASED", dsShardingProps));

表的配置

使用 ShardingTableRuleConfiguration 进行表的配置,第一个参数为逻辑表名,第二个参数为实际的数据节点.

ShardingTableRuleConfiguration orderTableRule = new ShardingTableRuleConfiguration("t_order", "ds_${0..3}.t_order_${0..5}");

策略配置

像下面这样,分别配置分库、分表、主键生成策略,并将这些策略设置到 ShardingTableRuleConfiguration 对象里.
要特别注意,策略中的算法名称一定要与前面算法列表(map)里的 key 一致.
在最后,将当前表的规则配置对象 ShardingTableRuleConfiguration 设置到整个配置的对象里 ShardingRuleConfiguration.

// 分库策略
ShardingStrategyConfiguration databaseShardingStrategy = new ComplexShardingStrategyConfiguration("user_id", "database_inline");
orderTableRule.setDatabaseShardingStrategy(databaseShardingStrategy);

// 分表策略
ShardingStrategyConfiguration tableShardingStrategy = new StandardShardingStrategyConfiguration("order_id", "table_inline");
orderTableRule.setTableShardingStrategy(tableShardingStrategy);

// 主键生成策略
KeyGenerateStrategyConfiguration keyGenerateStrategy = new KeyGenerateStrategyConfiguration("id", "snow_flake");
orderTableRule.setKeyGenerateStrategy(keyGenerateStrategy);

shardingRuleConfig.setTables(Collections.singletonList(orderTableRule));

初始化数据源

最后一步,将上面初始化好的数据库连接池,分片规则所有的信息,传入 ShardingSphere 提供的一个 dataSource 的工厂方法便可完成 dataSource 的创建.

由于我使用的是 Spring-Boot 框架,所以将数据源初始化为一个 Bean 即可,像下面这样:
(shardingRuleConfiguration()方法包含了我们上面配置的所有规则)

@Bean
public DataSource shardingDataSource() throws SQLException {

        Map<String, DataSource> dataSourceMap = new HashMap<>();

        for (int i = 0; i < shardingProperties.getTotal(); i++) {
            String dbName = String.format(shardingProperties.getNameTemplate(), i);
            DataSource dataSource = hikariDataSource(i);
            dataSourceMap.put(dbName, dataSource);
        }

        return ShardingSphereDataSourceFactory.createDataSource(dataSourceMap, Collections.singleton(shardingRuleConfiguration()), new Properties());
}
文章目录