ShardingSphere-JDBC实战

饭宝  金牌会员 | 2022-9-16 17:19:41 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 927|帖子 927|积分 2781

一、环境准备

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

数据库脚本:
  1. CREATE TABLE `product_order_0` (
  2.   `id` bigint NOT NULL AUTO_INCREMENT,
  3.   `out_trade_no` varchar(64) DEFAULT NULL COMMENT '订单唯一标识',
  4.   `state` varchar(11) DEFAULT NULL COMMENT 'NEW 未支付订单,PAY已经支付订单,CANCEL超时取消订单',
  5.   `create_time` datetime DEFAULT NULL COMMENT '订单生成时间',
  6.   `pay_amount` decimal(16,2) DEFAULT NULL COMMENT '订单实际支付价格',
  7.   `nickname` varchar(64) DEFAULT NULL COMMENT '昵称',
  8.   `user_id` bigint DEFAULT NULL COMMENT '用户id',
  9.   PRIMARY KEY (`id`)
  10. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
  11. CREATE TABLE `ad_config` (
  12.   `id` bigint unsigned NOT NULL COMMENT '主键id',
  13.   `config_key` varchar(1024) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '配置key',
  14.   `config_value` varchar(1024) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '配置value',
  15.   `type` varchar(128) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '类型',
  16.   PRIMARY KEY (`id`)
  17. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
  18. CREATE TABLE `product_order_item_0` (
  19.   `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  20.   `product_order_id` bigint DEFAULT NULL COMMENT '订单号',
  21.   `product_id` bigint DEFAULT NULL COMMENT '产品id',
  22.   `product_name` varchar(128) DEFAULT NULL COMMENT '商品名称',
  23.   `buy_num` int DEFAULT NULL COMMENT '购买数量',
  24.   `user_id` bigint DEFAULT NULL,
  25.   PRIMARY KEY (`id`)
  26. ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
复制代码
2.代码工程

1.工程创建


  • 创建Maven工程,添加相关Maven依赖,
  1. <properties>
  2.         <java.version>1.8</java.version>
  3.         <maven.compiler.source>11</maven.compiler.source>
  4.         <maven.compiler.target>11</maven.compiler.target>
  5.         <spring.boot.version>2.5.5</spring.boot.version>
  6.         <mybatisplus.boot.starter.version>3.4.0</mybatisplus.boot.starter.version>
  7.         <lombok.version>1.18.16</lombok.version>
  8.         <sharding-jdbc.version>4.1.1</sharding-jdbc.version>
  9.         <junit.version>4.12</junit.version>
  10.         <druid.version>1.1.16</druid.version>
  11.         
  12.         <skipTests>true</skipTests>
  13.     </properties>
  14.      <dependencies>
  15.         <dependency>
  16.             <groupId>org.springframework.boot</groupId>
  17.             <artifactId>spring-boot-starter-web</artifactId>
  18.             <version>${spring.boot.version}</version>
  19.         </dependency>
  20.         <dependency>
  21.             <groupId>org.springframework.boot</groupId>
  22.             <artifactId>spring-boot-starter-test</artifactId>
  23.             <version>${spring.boot.version}</version>
  24.             <scope>test</scope>
  25.         </dependency>
  26.         
  27.         <dependency>
  28.             <groupId>com.baomidou</groupId>
  29.             <artifactId>mybatis-plus-boot-starter</artifactId>
  30.             <version>${mybatisplus.boot.starter.version}</version>
  31.         </dependency>
  32.        
  33.         <dependency>
  34.             <groupId>mysql</groupId>
  35.             <artifactId>mysql-connector-java</artifactId>
  36.             <version>8.0.27</version>
  37.         </dependency>
  38.         <dependency>
  39.             <groupId>org.projectlombok</groupId>
  40.             <artifactId>lombok</artifactId>
  41.             <version>${lombok.version}</version>
  42.             
  43.         </dependency>
  44.        
  45.         <dependency>
  46.             <groupId>org.apache.shardingsphere</groupId>
  47.             <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
  48.             <version>${sharding-jdbc.version}</version>
  49.         </dependency>
  50.         <dependency>
  51.             <groupId>junit</groupId>
  52.             <artifactId>junit</artifactId>
  53.             <version>${junit.version}</version>
  54.         </dependency>
  55.     </dependencies>
  56.     <build>
  57.         <plugins>
  58.             <plugin>
  59.                 <groupId>org.springframework.boot</groupId>
  60.                 <artifactId>spring-boot-maven-plugin</artifactId>
  61.                 <version>${spring.boot.version}</version>
  62.                 <configuration>
  63.                     <fork>true</fork>
  64.                     <addResources>true</addResources>
  65.                 </configuration>
  66.             </plugin>
  67.         </plugins>
  68.     </build>
复制代码

  • 添加数据库配置文件,根据配置文件可知,配置了两个数据库ds0,ds1;
  1. spring.application.name=yb-sharding-jdbc
  2. server.port=8080
  3. logging.level.root=INFO
  4. # 打印执行的数据库以及语句
  5. spring.shardingsphere.props.sql.show=true
  6. # 数据源 ds0 ds1
  7. spring.shardingsphere.datasource.names=ds0,ds1
  8. # 第一个数据库
  9. spring.shardingsphere.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource
  10. spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.cj.jdbc.Driver
  11. 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
  12. spring.shardingsphere.datasource.ds0.username=root
  13. spring.shardingsphere.datasource.ds0.password=*****
  14. # 第二个数据库
  15. spring.shardingsphere.datasource.ds1.type=com.zaxxer.hikari.HikariDataSource
  16. spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.cj.jdbc.Driver
  17. 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
  18. spring.shardingsphere.datasource.ds1.username=root
  19. spring.shardingsphere.datasource.ds1.password=*****
复制代码
2.广播表介绍和配置实战


  • 指所有的分片数据源中都存在的表,表结构和表中的数据在每个数据库中均完全一致
  • 适用于数据量不大且需要与海量数据的表进行关联查询的场景
  • 例如:字典表、配置表

  • 添加AdConfigDO实体类和添加ProductOrderDOMapper类
  1. //数据库实体类
  2. @Data
  3. @EqualsAndHashCode(callSuper = false)
  4. @TableName("ad_config")
  5. public class AdConfigDO {
  6.     private Long id;
  7.     private String configKey;
  8.     private String configValue;
  9.     private String type;
  10. }
  11. //数据库实体配置类
  12. public interface AdConfigMapper extends BaseMapper<AdConfigDO> {
  13. }
复制代码

  • 设置ad_config为广播表,如果需要配置多个用 逗号 (,) 分开;设置id为生成算法为雪花算法。配置文件中添加如下代码,
  1. #配置广播表
  2. spring.shardingsphere.sharding.broadcast-tables=ad_config
  3. spring.shardingsphere.sharding.tables.ad_config.key-generator.column=id
  4. spring.shardingsphere.sharding.tables.ad_config.key-generator.type=SNOWFLAKE
复制代码

  • 添加测试方法
  1. @Test
  2. public void testSaveAdConfig(){
  3.     AdConfigDO adConfigDO = new AdConfigDO();
  4.     adConfigDO.setConfigKey("banner");
  5.     adConfigDO.setConfigValue("ybe.com");
  6.     adConfigDO.setType("ad");
  7.     adConfigMapper.insert(adConfigDO);
  8. }
复制代码

  • 执行结果,两个数据库的表都进行了更新。如下图


3.行表达式分片策略 InlineShardingStrategy


  • 只支持【单分片键】使用Groovy的表达式,提供对SQL语句中的 =和IN 的分片操作支持
  • 可以通过简单的配置使用,无需自定义分片算法,从而避免繁琐的Java代码开发

  • 添加ProductOrderDO实体类和添加ProductOrderDOMapper类
  1. //数据库实体类
  2. @Data
  3. @TableName("product_order")
  4. @EqualsAndHashCode(callSuper = false)
  5. public class ProductOrderDO {
  6.         // 不设置Mybatis-plus的主键规则,由sharding-jdbc 设置
  7.     private  Long  id;
  8.     private  String outTradeNo;
  9.     private String state;
  10.     private Date createTime;
  11.     private Double payAmount;
  12.     private String nickname;
  13.     private Long userId;
  14. }
  15. //数据库实体配置类
  16. public interface ProductOrderMapper extends BaseMapper<ProductOrderDO> {
  17. }
复制代码

  • 配置文件添加如下代码,
  1. # 指定product_order表的数据分布情况,配置数据节点,行表达式标识符使用 ${...} 或 $->{...},但前者与 Spring 本身的文件占位符冲突,所以在 Spring 环境中建议使用 $->{...}
  2. spring.shardingsphere.sharding.tables.product_order.actual-data-nodes=ds$->{0..1}.product_order_$->{0..1}
  3. #id生成策略
  4. spring.shardingsphere.sharding.tables.product_order.key-generator.column=id
  5. spring.shardingsphere.sharding.tables.product_order.key-generator.type=SNOWFLAKE
  6. #work_id 的设置
  7. spring.shardingsphere.sharding.tables.product_order.key-generator.props.worker.id=1
  8. #配置分库规则
  9. spring.shardingsphere.sharding.tables.product_order.database-strategy.inline.sharding-column=user_id
  10. spring.shardingsphere.sharding.tables.product_order.database-strategy.inline.algorithm-expression=ds$->{user_id % 2}
  11. #配置分表规则
  12. #指定product_order表的分片策略,分片策略包括【分片键和分片算法】
  13. spring.shardingsphere.sharding.tables.product_order.table-strategy.inline.sharding-column=id
  14. 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表。

  • 添加测试方法
  1. @Test
  2. public void testSaveProductOrder(){
  3.     Random random = new Random();
  4.     for (int i = 0 ;i < 10 ; i++){
  5.         // id是由配置的雪花算法生成
  6.         ProductOrderDO productOrderDO = new ProductOrderDO();
  7.         productOrderDO.setCreateTime(new Date());
  8.         productOrderDO.setNickname("ybe:"+i);
  9.         productOrderDO.setOutTradeNo(UUID.randomUUID().toString().substring(0,32));
  10.         productOrderDO.setPayAmount(100.00);
  11.         productOrderDO.setState("PAY");
  12.         // 随机生成UserId
  13.         productOrderDO.setUserId(Long.valueOf(random.nextInt(50)));
  14.         productOrderMapper.insert(productOrderDO);
  15.     }
  16. }
复制代码

  • 执行结果根据不同的user_id 和 id ,生成的表记录插入到了不同的库和表,如下图可以看到数据分散在了两个不同的数据库,以及不同的表中。




4.标准分片策略StandardShardingStrategy


  • 只支持【单分片键】,提供PreciseShardingAlgorithm和RangeShardingAlgorithm两个分片算法
  • PreciseShardingAlgorithm 精准分片 是必选的,用于处理=和IN的分片
  • RangeShardingAlgorithm 范围分片 是可选的,用于处理BETWEEN AND分片
  • 如果不配置RangeShardingAlgorithm,如果SQL中用了BETWEEN AND语法,则将按照全库路由处理,性能下降

  • 添加分表配置类CustomTablePreciseShardingAlgorithm
  1. public class CustomTablePreciseShardingAlgorithm implements PreciseShardingAlgorithm<Long> {
  2.     /**
  3.      * @param collection 数据源集合
  4.      *                    在分库时值为所有分片库的集合 databaseNames
  5.      *                   分表时为对应分片库中所有分片表的集合 tablesNames
  6.      * @param preciseShardingValue 分片属性,包括
  7.      *                                    logicTableName 为逻辑表,
  8.      *                                    columnName 分片健(字段),
  9.      *                                    value 为从 SQL 中解析出的分片健的值
  10.      * @return
  11.      */
  12.     @Override
  13.     public String doSharding(Collection<String> collection, PreciseShardingValue<Long> preciseShardingValue) {
  14.         //循环遍历 数据源,根据算法
  15.         for (String databaseName : collection) {
  16.             String value = preciseShardingValue.getValue() % collection.size() + "";
  17.             //value是0,则进入0库表,1则进入1库表
  18.             if (databaseName.endsWith(value)) {
  19.                 return databaseName;
  20.             }
  21.         }
  22.         throw new IllegalArgumentException();
  23.     }
  24. }
复制代码

  • 添加分库配置类CustomDBPreciseShardingAlgorithm
  1. /**
  2.      * @param collection 数据源集合
  3.      *                    在分库时值为所有分片库的集合 databaseNames
  4.      *                   分表时为对应分片库中所有分片表的集合 tablesNames
  5.      * @param preciseShardingValue 分片属性,包括
  6.      *                                    logicTableName 为逻辑表,
  7.      *                                    columnName 分片健(字段),
  8.      *                                    value 为从 SQL 中解析出的分片健的值
  9.      * @return
  10.      */
  11. @Override
  12. public String doSharding(Collection<String> collection, PreciseShardingValue<Long> preciseShardingValue) {
  13.     for (String databaseName : collection) {
  14.         String value = preciseShardingValue.getValue() % collection.size() + "";
  15.         //value是0,则进入0库表,1则进入1库表
  16.         if (databaseName.endsWith(value)) {
  17.             return databaseName;
  18.         }
  19.     }
  20.     throw new IllegalArgumentException();
  21. }
复制代码

  • 添加分表范围配置类CustomRangeShardingAlgorithm
  1. public class CustomRangeShardingAlgorithm implements RangeShardingAlgorithm<Long> {
  2.     /**
  3.      * @param collection 数据源集合
  4.      *                    在分库时值为所有分片库的集合 databaseNames
  5.      *                   分表时为对应分片库中所有分片表的集合 tablesNames
  6.      * @param rangeShardingValue 分片属性,包括
  7.      *                                    logicTableName 为逻辑表,
  8.      *                                    columnName 分片健(字段),
  9.      *                                    value 为从 SQL 中解析出的分片健的值
  10.      * @return
  11.      */
  12.     @Override
  13.     public Collection<String> doSharding(Collection<String> collection, RangeShardingValue<Long> rangeShardingValue) {
  14.         Set<String> result = new LinkedHashSet<>();
  15.         // between 起始值
  16.         Long lower = rangeShardingValue.getValueRange().lowerEndpoint();
  17.         // between 结束值
  18.         Long upper = rangeShardingValue.getValueRange().upperEndpoint();
  19.         // 循环范围计算分库逻辑
  20.         for (long i = lower; i <= upper; i++) {
  21.             for (String databaseName : collection) {
  22.                 if (databaseName.endsWith(i % collection.size() + "")) {
  23.                     result.add(databaseName);
  24.                 }
  25.             }
  26.         }
  27.         return result;
  28.     }
  29. }
复制代码

  • 添加测试方法
  1. # 分库分片算法
  2. spring.shardingsphere.sharding.tables.product_order.database-strategy.standard.sharding-column=user_id
  3. spring.shardingsphere.sharding.tables.product_order.database-strategy.standard.precise-algorithm-class-name=com.ybe.algorithm.CustomDBPreciseShardingAlgorithm
  4. #精准水平分表下,增加一个范围分片
  5. spring.shardingsphere.sharding.tables.product_order.table-strategy.standard.range-algorithm-class-name=com.ybe.algorithm.CustomRangeShardingAlgorithm
  6. # 分表分片健
  7. spring.shardingsphere.sharding.tables.product_order.table-strategy.standard.sharding-column=id
  8. spring.shardingsphere.sharding.tables.product_order.table-strategy.standard.precise-algorithm-class-name=com.ybe.algorithm.CustomTablePreciseShardingAlgorithm
复制代码

  • 执行结果:

    • 添加绑定表配置之前,可以看到查询的sql语句,主表和子表是笛卡尔积的关联关系。如下图,


    • 添加绑定表配置之后,可以看到查询的sql语句,主表和子表是一一对应的。如下图,



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

饭宝

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表