设计订单系统有两个数据库db_order_01和db_order_02。每个数据库分别有t_order_0和t_order_1两张订单表。
订单表设计有订单ID(order_id),用户ID(user_id),商户ID(merchant_id)。假设商户查看订单操作要比用户查看订单的操作更加频繁。避免商户查询订单时跨库查询,用商户ID对数据库数量取模的方式将同一个商户的订单路由到同一个数据库中。通过对用户ID进行Hash后取模,将同一个用户的订单路由到同一张表中。以上设计主要是为了使用取模算法和哈希取模算法。
订单数据库和表创建DDL: t_order.sql
使用的Spring boot、ShardingSphere-JDBC和MyBatis的依赖如下:
org.springframework.boot
spring-boot-dependencies
2.6.8
pom
import
org.apache.shardingsphere
shardingsphere-jdbc-core
5.2.0
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.2.2
/**
* HikariConfig
* @param database db
* @return dataSource config
*/
private HikariConfig create(String database) {
Assert.hasLength(database, "database require not null");
HikariConfig config = new HikariConfig();
config.setUsername(USERNAME);
config.setPassword(PASSWORD);
config.setDriverClassName(DRIVER_NAME);
config.setJdbcUrl(String.format("jdbc:mysql://localhost:3306/%s", database));
return config;
}
/**
* create DataSource
* @param database db
* @return dataSource
*/
private DataSource createDataSource(String database) {
return new HikariDataSource(this.create(database));
}
@Bean
public DataSource dbOrder01DataSource() {
return this.createDataSource("db_order_01");
}
@Bean
public DataSource dbOrder02DataSource() {
return new HikariDataSource(this.create("db_order_02"));
}
/**
* 初始化数据源
* @return dataSource map
*/
private Map initDataSourceMap() {
Map dataSourceMap = new HashMap<>();
// order databases
dataSourceMap.put("db_order_0", this.dbOrder01DataSource());
dataSourceMap.put("db_order_1", this.dbOrder02DataSource());
return dataSourceMap;
}
请注意数据源集合中database的key,该key在后面的表分片规则时指定实际数据节点时需要使用。
@Bean
@Primary
public DataSource shardingDateSource() {
// 分片规则集
List dbRules = new ArrayList<>();
ShardingRuleConfiguration shardingRule = new ShardingRuleConfiguration();
// 设置默认的分片键,同一个商户的订单放到一个数据库中
String defaultShardingColumn = "merchant_id";
shardingRule.setDefaultShardingColumn(defaultShardingColumn);
// 创建默认database分片策略
shardingRule.setDefaultDatabaseShardingStrategy(this.createStandardShardingStrategy(
defaultShardingColumn, "MOD"));
// 创建默认的表分片策略,同一个用户的订单放到一张表中
String userIdShardingColumn = "user_id";
shardingRule.setDefaultTableShardingStrategy(this.createStandardShardingStrategy(
userIdShardingColumn, "HASH_MOD"));
// 分片算法properties属性配置
Map shardingAlgorithmsConf = shardingRule.getShardingAlgorithms();
// 设置分片算法属性配置
shardingAlgorithmsConf.putAll(this.shardingAlgoConfig());
// key gen算法属性配置
Map keyGenerators = shardingRule.getKeyGenerators();
keyGenerators.putAll(this.keyGenAlgoConfig());
// order table sharding 配置
ShardingTableRuleConfiguration orderTableRule = new ShardingTableRuleConfiguration(
"t_order", "db_order_$->{0..1}.t_order_$->{0..1}");
// order_id gen
KeyGenerateStrategyConfiguration orderIdGen =
new KeyGenerateStrategyConfiguration("order_id", "SNOWFLAKE");
orderTableRule.setKeyGenerateStrategy(orderIdGen);
List tableRuleConfigs = new ArrayList<>();
// add t_order rule
tableRuleConfigs.add(orderTableRule);
// config t_user table sharding rule
ShardingTableRuleConfiguration tUserTableShardingRule = this.configUserTableShardingRule();
// add t_user rule
tableRuleConfigs.add(tUserTableShardingRule);
// set tables rule
shardingRule.getTables().addAll(tableRuleConfigs);
dbRules.add(shardingRule);
DataSource primaryDataSource = null;
try {
ModeConfiguration modeConfiguration = this.createModeConfiguration();
primaryDataSource = ShardingSphereDataSourceFactory.createDataSource(modeConfiguration,
this.initDataSourceMap(), dbRules, this.shadingDataSourceProps());
} catch (SQLException e) {
LOGGER.error("create primaryDataSource error:", e);
}
return Objects.requireNonNull(primaryDataSource, "primaryDataSource require not null");
}
配置表分片规则(ShardingTableRuleConfiguration)时,存在多个数据源和多个表分片时,可以使用Groovy表达式。简单配置示例如下:
注意actualDataNodes的配置,如果配置不正确,在执行SQL时会出现找不到路由数据源的异常。
其中MyBatis的配置这里就不就行介绍了。源码地址: https://gitee.com/qinshizhang/shard-jdbc
下载后简单配置一个数据库信息即可启动并运行。
@Test
void insertSelective() {
this.checkMapper();
OrderEntity order = new OrderEntity();
order.setUserId(userIdAlgo.generate());
order.setMerchantId(localRandom.nextLong(47));
order.setOrderId((Long) orderIdGenAlgo.generateKey());
int insertRows = orderMapper.insertSelective(order);
assertEquals(1, insertRows);
}
测试执行结果如下:
其中商户ID=6,对2取模结果为0,数据路由到db_order_0数据库中。用户ID是468323156650622977,该值hash对2取模结果为1,数据路由到t_order_1表中。
到此,第一个使用ShardingSphere-JDBC进行订单数据进行分库分表的示例完成了,后续逐渐加入用户、产品、库存等表的操作。一起加入学习吧!!