初探Sharding-JDBC订单表分片实现

设计订单系统有两个数据库db_order_01和db_order_02。每个数据库分别有t_order_0和t_order_1两张订单表。

初探Sharding-JDBC订单表分片实现_第1张图片

订单表设计有订单ID(order_id),用户ID(user_id),商户ID(merchant_id)。假设商户查看订单操作要比用户查看订单的操作更加频繁。避免商户查询订单时跨库查询,用商户ID对数据库数量取模的方式将同一个商户的订单路由到同一个数据库中。通过对用户ID进行Hash后取模,将同一个用户的订单路由到同一张表中。以上设计主要是为了使用取模算法和哈希取模算法。

订单数据库和表创建DDL: t_order.sql

一、ShardingSphereDataSource数据源配置

使用的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
            
        
    

(1)创建db_order_01和db_order_02数据源

    /**
     * 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"));
    }

(2)初始化数据源集合

   /**
     * 初始化数据源
     * @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在后面的表分片规则时指定实际数据节点时需要使用。

(3)配置分片规则并创建ShardingSphereDataSource

    @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

下载后简单配置一个数据库信息即可启动并运行。

(1)测试新增订单数据 

    @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);
    }

测试执行结果如下: 

初探Sharding-JDBC订单表分片实现_第2张图片

 其中商户ID=6,对2取模结果为0,数据路由到db_order_0数据库中。用户ID是468323156650622977,该值hash对2取模结果为1,数据路由到t_order_1表中。

到此,第一个使用ShardingSphere-JDBC进行订单数据进行分库分表的示例完成了,后续逐渐加入用户、产品、库存等表的操作。一起加入学习吧!!

你可能感兴趣的:(spring,boot,MyBatis,ShardingSphere,MySQL)