创建2个库2个表:
数据库脚本:
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;
<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> <!--mybatis plus和springboot整合--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>${mybatisplus.boot.starter.version}</version> </dependency> <!-- mysql数据库 --> <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> <!--<scope>provided</scope>--> </dependency> <!-- shardingshpere-jdbc--> <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>
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=*****
//数据库实体类 @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> { }
#配置广播表 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); }
//数据库实体类 @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); } }
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(); } }
/** * @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(); }
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
@Test public void testRand(){ Random random = new Random(); for (int i = 0 ;i < 10 ; i++){ 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"); productOrderDO.setUserId(Long.valueOf(random.nextInt(50))); productOrderMapper.insert(productOrderDO); } productOrderMapper.selectList(new QueryWrapper<ProductOrderDO>().between("id",1L,1L)); }
public class CustomComplexKeysShardingAlgorithm implements ComplexKeysShardingAlgorithm<Long> { @Override public Collection<String> doSharding(Collection<String> collection, ComplexKeysShardingValue<Long> complexKeysShardingValue) { // 得到每个分片健对应的值 Collection<Long> orderIdValues = this.getShardingValue(complexKeysShardingValue, "id"); Collection<Long> userIdValues = this.getShardingValue(complexKeysShardingValue, "user_id"); List<String> shardingSuffix = new ArrayList<>(); // 对两个分片健取模的方式 for (Long userId : userIdValues) { for (Long orderId : orderIdValues) { String suffix = userId % 2 + "_" + orderId % 2; for (String databaseName : collection) { if (databaseName.endsWith(suffix)) { shardingSuffix.add(databaseName); } } } } return shardingSuffix; } /** * shardingValue 分片属性,包括 * logicTableName 为逻辑表, * columnNameAndShardingValuesMap 存储多个分片健 包括key-value * key:分片key,id和user_id * value:分片value,66和99 * * @return shardingValues 集合 */ private Collection<Long> getShardingValue(ComplexKeysShardingValue<Long> shardingValues, final String key) { Collection<Long> valueSet = new ArrayList<>(); Map<String, Collection<Long>> columnNameAndShardingValuesMap = shardingValues.getColumnNameAndShardingValuesMap(); if (columnNameAndShardingValuesMap.containsKey(key)) { valueSet.addAll(columnNameAndShardingValuesMap.get(key)); } return valueSet; }
# 复合分片算法,order_id,user_id 同时作为分片健 spring.shardingsphere.sharding.tables.product_order.table-strategy.complex.sharding-columns=user_id,id spring.shardingsphere.sharding.tables.product_order.table-strategy.complex.algorithm-class-name=com.ybe.algorithm.CustomComplexKeysShardingAlgorithm
@Test public void testComplex(){ productOrderMapper.selectList(new QueryWrapper<ProductOrderDO>().eq("id",66L).eq("user_id",99L)); }
public class CustomTableHintShardingAlgorithm implements HintShardingAlgorithm<Long> { @Override public Collection<String> doSharding(Collection<String> collection, HintShardingValue<Long> hintShardingValue) { Collection<String> result = new ArrayList<>(); for (String tableName : collection) { for (Long shardingValue : hintShardingValue.getValues()) { if (tableName.endsWith(String.valueOf(shardingValue % collection.size()))) { result.add(tableName); } } } return result; } }
public class CustomDBHintShardingAlgorithm implements HintShardingAlgorithm<Long> { @Override public Collection<String> doSharding(Collection<String> collection, HintShardingValue<Long> hintShardingValue) { Collection<String> result = new ArrayList<>(); for (String dbName : collection) { for (Long shardingValue : hintShardingValue.getValues()) { if (dbName.endsWith(String.valueOf(shardingValue % collection.size()))) { result.add(dbName); } } } return result; } }
# Hint分片算法 spring.shardingsphere.sharding.tables.product_order.table-strategy.hint.algorithm-class-name=com.ybe.algorithm.CustomTableHintShardingAlgorithm spring.shardingsphere.sharding.tables.product_order.database-strategy.hint.algorithm-class-name=com.ybe.algorithm.CustomDBHintShardingAlgorithm
@Test public void testHint(){ // 清除掉历史的规则 HintManager.clear(); //Hint分片策略必须要使用 HintManager工具类 HintManager hintManager = HintManager.getInstance(); // 设置库的分片健,value用于库分片取模, hintManager.addDatabaseShardingValue("product_order",4L); // 设置表的分片健,value用于表分片取模, hintManager.addTableShardingValue("product_order", 5L); //对应的value只做查询,不做sql解析 productOrderMapper.selectList(new QueryWrapper<ProductOrderDO>().eq("id", 66L)); }
//数据库实体类 @Data @TableName("product_order_item") @EqualsAndHashCode(callSuper = false) public class ProductOrderItemDO { private Long id; private Long productOrderId; private Long productId; private String productName; private Integer buyNum; private Long userId; } //数据库实体配置类 public interface ProductOrderItemMapper extends BaseMapper<ProductOrderItemDO> { }
spring.shardingsphere.sharding.tables.product_order_item.actual-data-nodes=ds$->{0..1}.product_order_item_$->{0..1} # 指定product_order表的分片策略,分片策略包括【分片键和分片算法】 spring.shardingsphere.sharding.tables.product_order_item.table-strategy.inline.sharding-column=product_order_id spring.shardingsphere.sharding.tables.product_order_item.table-strategy.inline.algorithm-expression=product_order_item_$->{product_order_id % 2} #配置绑定表 spring.shardingsphere.sharding.binding-tables[0]=product_order,product_order_item
@Test public void testBinding(){ List<Object> objects = productOrderMapper.listProductOrderDetail(); System.out.println(objects); }
执行结果: