ToB企服应用市场:ToB评测及商务社交产业平台
标题:
ShardingSphere-JDBC实战
[打印本页]
作者:
饭宝
时间:
2022-9-16 17:19
标题:
ShardingSphere-JDBC实战
一、环境准备
1.数据库
创建2个库2个表:
xdclass_shop_order_0
product_order_0
product_order_1
ad_config
product_order_item_0
product_order_item_1
xdclass_shop_order_1
product_order_0
product_order_1
ad_config
product_order_item_0
product_order_item_1
数据库脚本:
CREATE TABLE `product_order_0` (
`id` bigint NOT NULL AUTO_INCREMENT,
`out_trade_no` varchar(64) DEFAULT NULL COMMENT '订单唯一标识',
`state` varchar(11) DEFAULT NULL COMMENT 'NEW 未支付订单,PAY已经支付订单,CANCEL超时取消订单',
`create_time` datetime DEFAULT NULL COMMENT '订单生成时间',
`pay_amount` decimal(16,2) DEFAULT NULL COMMENT '订单实际支付价格',
`nickname` varchar(64) DEFAULT NULL COMMENT '昵称',
`user_id` bigint DEFAULT NULL COMMENT '用户id',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
CREATE TABLE `ad_config` (
`id` bigint unsigned NOT NULL COMMENT '主键id',
`config_key` varchar(1024) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '配置key',
`config_value` varchar(1024) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '配置value',
`type` varchar(128) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '类型',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
CREATE TABLE `product_order_item_0` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`product_order_id` bigint DEFAULT NULL COMMENT '订单号',
`product_id` bigint DEFAULT NULL COMMENT '产品id',
`product_name` varchar(128) DEFAULT NULL COMMENT '商品名称',
`buy_num` int DEFAULT NULL COMMENT '购买数量',
`user_id` bigint DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
复制代码
2.代码工程
1.工程创建
创建Maven工程,添加相关Maven依赖,
<properties>
<java.version>1.8</java.version>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<spring.boot.version>2.5.5</spring.boot.version>
<mybatisplus.boot.starter.version>3.4.0</mybatisplus.boot.starter.version>
<lombok.version>1.18.16</lombok.version>
<sharding-jdbc.version>4.1.1</sharding-jdbc.version>
<junit.version>4.12</junit.version>
<druid.version>1.1.16</druid.version>
<skipTests>true</skipTests>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring.boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${spring.boot.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatisplus.boot.starter.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>${sharding-jdbc.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
<configuration>
<fork>true</fork>
<addResources>true</addResources>
</configuration>
</plugin>
</plugins>
</build>
复制代码
添加数据库配置文件,根据配置文件可知,配置了两个数据库ds0,ds1;
spring.application.name=yb-sharding-jdbc
server.port=8080
logging.level.root=INFO
# 打印执行的数据库以及语句
spring.shardingsphere.props.sql.show=true
# 数据源 ds0 ds1
spring.shardingsphere.datasource.names=ds0,ds1
# 第一个数据库
spring.shardingsphere.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds0.jdbc-url=jdbc:mysql://localhost:3306/ybe_shop_order0?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
spring.shardingsphere.datasource.ds0.username=root
spring.shardingsphere.datasource.ds0.password=*****
# 第二个数据库
spring.shardingsphere.datasource.ds1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds1.jdbc-url=jdbc:mysql://localhost:3306/ybe_shop_order1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=*****
复制代码
2.广播表介绍和配置实战
指所有的分片数据源中都存在的表,表结构和表中的数据在每个数据库中均完全一致
适用于数据量不大且需要与海量数据的表进行关联查询的场景
例如:字典表、配置表
添加AdConfigDO实体类和添加ProductOrderDOMapper类
//数据库实体类
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("ad_config")
public class AdConfigDO {
private Long id;
private String configKey;
private String configValue;
private String type;
}
//数据库实体配置类
public interface AdConfigMapper extends BaseMapper<AdConfigDO> {
}
复制代码
设置ad_config为广播表,如果需要配置多个用 逗号 (,) 分开;设置id为生成算法为雪花算法。配置文件中添加如下代码,
#配置广播表
spring.shardingsphere.sharding.broadcast-tables=ad_config
spring.shardingsphere.sharding.tables.ad_config.key-generator.column=id
spring.shardingsphere.sharding.tables.ad_config.key-generator.type=SNOWFLAKE
复制代码
添加测试方法
@Test
public void testSaveAdConfig(){
AdConfigDO adConfigDO = new AdConfigDO();
adConfigDO.setConfigKey("banner");
adConfigDO.setConfigValue("ybe.com");
adConfigDO.setType("ad");
adConfigMapper.insert(adConfigDO);
}
复制代码
执行结果,两个数据库的表都进行了更新。如下图
3.行表达式分片策略 InlineShardingStrategy
只支持【
单分片键
】使用Groovy的表达式,提供对SQL语句中的 =和IN 的分片操作支持
可以通过简单的配置使用,无需自定义分片算法,从而避免繁琐的Java代码开发
添加ProductOrderDO实体类和添加ProductOrderDOMapper类
//数据库实体类
@Data
@TableName("product_order")
@EqualsAndHashCode(callSuper = false)
public class ProductOrderDO {
// 不设置Mybatis-plus的主键规则,由sharding-jdbc 设置
private Long id;
private String outTradeNo;
private String state;
private Date createTime;
private Double payAmount;
private String nickname;
private Long userId;
}
//数据库实体配置类
public interface ProductOrderMapper extends BaseMapper<ProductOrderDO> {
}
复制代码
配置文件添加如下代码,
# 指定product_order表的数据分布情况,配置数据节点,行表达式标识符使用 ${...} 或 $->{...},但前者与 Spring 本身的文件占位符冲突,所以在 Spring 环境中建议使用 $->{...}
spring.shardingsphere.sharding.tables.product_order.actual-data-nodes=ds$->{0..1}.product_order_$->{0..1}
#id生成策略
spring.shardingsphere.sharding.tables.product_order.key-generator.column=id
spring.shardingsphere.sharding.tables.product_order.key-generator.type=SNOWFLAKE
#work_id 的设置
spring.shardingsphere.sharding.tables.product_order.key-generator.props.worker.id=1
#配置分库规则
spring.shardingsphere.sharding.tables.product_order.database-strategy.inline.sharding-column=user_id
spring.shardingsphere.sharding.tables.product_order.database-strategy.inline.algorithm-expression=ds$->{user_id % 2}
#配置分表规则
#指定product_order表的分片策略,分片策略包括【分片键和分片算法】
spring.shardingsphere.sharding.tables.product_order.table-strategy.inline.sharding-column=id
spring.shardingsphere.sharding.tables.product_order.table-strategy.inline.algorithm-expression=product_order_$->{id % 2}
复制代码
由配置文件可知,
设置了product_order为逻辑表,设置了它的真实数据节点为ds$->{0..1}.product_order_$->{0..1},使用了表达式$->{...},它表示实际的物理表为:ds0.product_order_0,ds0.product_order_1,ds1.product_order_0,ds1.product_order_1,总共对应了2个库的2个物理表。
设置了product_order表的id计算方式为雪花算法;
设置了product_order表的分库规则,分库规则为 user_id % 2;也就是说会根据user_id % 2的结果确定是ds0库还是ds1库。
设置了product_order表的分表规则,分表规则为 id % 2;也就是说会根据id % 2的结果确定是product_order_0表还是product_order_1表。
添加测试方法
@Test
public void testSaveProductOrder(){
Random random = new Random();
for (int i = 0 ;i < 10 ; i++){
// id是由配置的雪花算法生成
ProductOrderDO productOrderDO = new ProductOrderDO();
productOrderDO.setCreateTime(new Date());
productOrderDO.setNickname("ybe:"+i);
productOrderDO.setOutTradeNo(UUID.randomUUID().toString().substring(0,32));
productOrderDO.setPayAmount(100.00);
productOrderDO.setState("PAY");
// 随机生成UserId
productOrderDO.setUserId(Long.valueOf(random.nextInt(50)));
productOrderMapper.insert(productOrderDO);
}
}
复制代码
执行结果根据不同的user_id 和 id ,生成的表记录插入到了不同的库和表,如下图可以看到数据分散在了两个不同的数据库,以及不同的表中。
4.标准分片策略StandardShardingStrategy
只支持【
单分片键
】,提供PreciseShardingAlgorithm和RangeShardingAlgorithm两个分片算法
PreciseShardingAlgorithm 精准分片 是必选的,用于处理=和IN的分片
RangeShardingAlgorithm 范围分片 是可选的,用于处理BETWEEN AND分片
如果不配置RangeShardingAlgorithm,如果SQL中用了BETWEEN AND语法,则将按照全库路由处理,性能下降
添加分表配置类CustomTablePreciseShardingAlgorithm
public class CustomTablePreciseShardingAlgorithm implements PreciseShardingAlgorithm<Long> {
/**
* @param collection 数据源集合
* 在分库时值为所有分片库的集合 databaseNames
* 分表时为对应分片库中所有分片表的集合 tablesNames
* @param preciseShardingValue 分片属性,包括
* logicTableName 为逻辑表,
* columnName 分片健(字段),
* value 为从 SQL 中解析出的分片健的值
* @return
*/
@Override
public String doSharding(Collection<String> collection, PreciseShardingValue<Long> preciseShardingValue) {
//循环遍历 数据源,根据算法
for (String databaseName : collection) {
String value = preciseShardingValue.getValue() % collection.size() + "";
//value是0,则进入0库表,1则进入1库表
if (databaseName.endsWith(value)) {
return databaseName;
}
}
throw new IllegalArgumentException();
}
}
复制代码
添加分库配置类CustomDBPreciseShardingAlgorithm
/**
* @param collection 数据源集合
* 在分库时值为所有分片库的集合 databaseNames
* 分表时为对应分片库中所有分片表的集合 tablesNames
* @param preciseShardingValue 分片属性,包括
* logicTableName 为逻辑表,
* columnName 分片健(字段),
* value 为从 SQL 中解析出的分片健的值
* @return
*/
@Override
public String doSharding(Collection<String> collection, PreciseShardingValue<Long> preciseShardingValue) {
for (String databaseName : collection) {
String value = preciseShardingValue.getValue() % collection.size() + "";
//value是0,则进入0库表,1则进入1库表
if (databaseName.endsWith(value)) {
return databaseName;
}
}
throw new IllegalArgumentException();
}
复制代码
添加分表范围配置类CustomRangeShardingAlgorithm
public class CustomRangeShardingAlgorithm implements RangeShardingAlgorithm<Long> {
/**
* @param collection 数据源集合
* 在分库时值为所有分片库的集合 databaseNames
* 分表时为对应分片库中所有分片表的集合 tablesNames
* @param rangeShardingValue 分片属性,包括
* logicTableName 为逻辑表,
* columnName 分片健(字段),
* value 为从 SQL 中解析出的分片健的值
* @return
*/
@Override
public Collection<String> doSharding(Collection<String> collection, RangeShardingValue<Long> rangeShardingValue) {
Set<String> result = new LinkedHashSet<>();
// between 起始值
Long lower = rangeShardingValue.getValueRange().lowerEndpoint();
// between 结束值
Long upper = rangeShardingValue.getValueRange().upperEndpoint();
// 循环范围计算分库逻辑
for (long i = lower; i <= upper; i++) {
for (String databaseName : collection) {
if (databaseName.endsWith(i % collection.size() + "")) {
result.add(databaseName);
}
}
}
return result;
}
}
复制代码
添加测试方法
# 分库分片算法
spring.shardingsphere.sharding.tables.product_order.database-strategy.standard.sharding-column=user_id
spring.shardingsphere.sharding.tables.product_order.database-strategy.standard.precise-algorithm-class-name=com.ybe.algorithm.CustomDBPreciseShardingAlgorithm
#精准水平分表下,增加一个范围分片
spring.shardingsphere.sharding.tables.product_order.table-strategy.standard.range-algorithm-class-name=com.ybe.algorithm.CustomRangeShardingAlgorithm
# 分表分片健
spring.shardingsphere.sharding.tables.product_order.table-strategy.standard.sharding-column=id
spring.shardingsphere.sharding.tables.product_order.table-strategy.standard.precise-algorithm-class-name=com.ybe.algorithm.CustomTablePreciseShardingAlgorithm
复制代码
执行结果:
添加绑定表配置之前,可以看到查询的sql语句,主表和子表是笛卡尔积的关联关系。如下图,
添加绑定表配置之后,可以看到查询的sql语句,主表和子表是一一对应的。如下图,
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/)
Powered by Discuz! X3.4