SpringBoot整合Sharding-JDBC水平分表

本文使用Sharding-JDBC完成对订单表的水平分表,通过快速入门程序的开发,快速体验Sharding-JDBC的使用方法。shardingsphere用户手册

首先创建两张表,t_order_1和t_order_2,这两张表是订单表拆分后的表,通过Sharding-Jdbc向订单表插入数据,按照一定的分片规则,主键为偶数的进入t_order_1,另一部分数据进入t_order_2,通过Sharding-Jdbc 查询数据,根据 SQL语句的内容从t_order_1或t_order_2查询数据。

环境搭建

环境说明

操作系统:Win10

数据库:MySQL-5.7.38

JDK:64位 jdk1.8.0_152

应用框架:spring-boot-2.3.3.RELEASE

创建数据库

创建订单库order_db

CREATE DATABASE `order_db` CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';

在order_db中创建t_order_1、t_order_2表

DROP TABLE IF EXISTS `t_order_1`;
CREATE TABLE `t_order_1` (
`order_id` bigint(20) NOT NULL COMMENT '订单id',
`price` decimal(10, 2) NOT NULL COMMENT '订单价格',
`user_id` bigint(20) NOT NULL COMMENT '下单用户id',
`status` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '订单状态',
PRIMARY KEY (`order_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
DROP TABLE IF EXISTS `t_order_2`;
CREATE TABLE `t_order_2` (
`order_id` bigint(20) NOT NULL COMMENT '订单id',
`price` decimal(10, 2) NOT NULL COMMENT '订单价格',
`user_id` bigint(20) NOT NULL COMMENT '下单用户id',
`status` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '订单状态',
PRIMARY KEY (`order_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;   

引入maven依赖

<dependencies>
    <!-- starter-web:spring-webmvc + autoconfigure + logback + yaml + tomcat -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- starter-test:junit + spring-test + mockito -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <!-- <version>5.1.49</version>-->
        <version>8.0.28</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>

    <!-- 实现对 Sharding-JDBC 的自动化配置 -->
    <dependency>
        <groupId>org.apache.shardingsphere</groupId>
        <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
        <version>5.1.1</version>
    </dependency>

    <!-- mybatis -->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.1.3</version>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.24</version>
    </dependency>
</dependencies>
<build>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.yml</include>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.yml</include>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
    </resources>
</build>

编写程序

分片规则配置

分片规则配置是sharding-jdbc进行对分库分表操作的重要依据,配置内容包括:数据源、主键生成策略、分片策略等。

在application.properties中配置:

server.port=56081
server.servlet.encoding.enabled=true
server.servlet.encoding.charset=utf-8
server.servlet.encoding.force=true
spring.main.allow‐bean‐definition‐overriding=true
#mybatis配置
mybatis.configuration.map‐underscore‐to‐camel‐case=true
mybatis.mapper-locations=classpath*:mapper/**/*Mapper.xml
# ============================== 定义数据源 start ============================
spring.shardingsphere.datasource.names=m1
#type属性不能为空,否则汇报NullPointerException
spring.shardingsphere.datasource.m1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.m1.driverClassName=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m1.jdbcUrl=jdbc:mysql://127.0.0.1:3306/order_db?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
spring.shardingsphere.datasource.m1.username=root
spring.shardingsphere.datasource.m1.password=123456
#数据库连接池的其它属性......
# ============================== 定义数据源 end ============================
# ============================== 数据分片规则 start  ============================
# 指定t_order表的数据分布情况,配置数据节点,规则由数据源名 + 表名组成
spring.shardingsphere.rules.sharding.tables.t_order.actual-data-nodes=m1.t_order_$->{1..2}
# 主键生成策略
# 指定t_order表的主键生成策略为SNOWFLAKE
# 如果是自动生成的,在插入数据的sql中就不要传id,null也不行,直接插入字段中就不要有主键的字段
spring.shardingsphere.rules.sharding.tables.t_order.key-generate-strategy.column=order_id
spring.shardingsphere.rules.sharding.tables.t_order.key-generate-strategy.key-generator-name=snowflake
# snowflake与上面key-generator-name定义的名称保持一致
spring.shardingsphere.rules.sharding.key-generators.snowflake.type=SNOWFLAKE
# 指定分表策略,
# standard:用于单分片键的标准分片场景
# complex:用于多分片键的复合分片场景
# hint:Hint 分片策略
# none不分片
# 指定t_order表分片的字段
spring.shardingsphere.rules.sharding.tables.t_order.table-strategy.standard.sharding-column=order_id
# 指定t_order表分片算法名称
spring.shardingsphere.rules.sharding.tables.t_order.table-strategy.standard.sharding-algorithm-name=t-order-inline
# t_order表分片算法配置
# 分片算法类型
spring.shardingsphere.rules.sharding.sharding-algorithms.t-order-inline.type=INLINE
# 分片算法属性配置, 配置INLINE表达式
spring.shardingsphere.rules.sharding.sharding-algorithms.t-order-inline.props.algorithm-expression=t_order_$->{order_id % 2 + 1}
# ============================== 数据分片规则 end  ============================
# 打开sql输出日志
spring.shardingsphere.props.sql.show=true

配置参数说明:

  • 首先定义数据源m1,并对m1进行实际的参数配置。
  • 指定t_order表的数据分布情况,它分布在m1.t_order_1,m1.t_order_2
  • 指定t_order表的主键生成策略为SNOWFLAKE,SNOWFLAKE是一种分布式自增算法,保证id全局唯一
  • 定义t_order分片策略,order_id为偶数的数据落在t_order_1,为奇数的落在t_order_2,分表策略的表达式为t_order_$->{order_id % 2 + 1}

数据操作

@Mapper
public interface OrderDao {
    /**
     * 新增订单
     *
     * @param price  订单价格
     * @param userId 用户id
     * @param status 订单状态
     * @return
     */
    @Insert("insert into t_order(price,user_id,status) value(#{price},#{userId},#{status})")
    int insertOrder(@Param("price") BigDecimal price, @Param("userId") Long userId,
                    @Param("status") String status);

    /**
     * 根据id列表查询多个订单
     *
     * @param orderIds 订单id列表
     * @return
     */
    @Select({"<script>" +
            "select " +
            " * " +
            " from t_order t" +
            " where t.order_id in " +
            "<foreach collection='orderIds' item='id' open='(' separator=',' close=')'>" +
            " #{id} " +
            "</foreach>" +
            "</script>"})
    List<Map> selectOrderByIds(@Param("orderIds") List<Long> orderIds);
}

启动类

@SpringBootApplication
@MapperScan("com.harvey.main.mapper")
public class ShardJdbcMain {
    public static void main(String[] args) {
        SpringApplication.run(ShardJdbcMain.class, args);
    }
}

测试

编写单元测试

@SpringBootTest(classes = {ShardJdbcMain.class})
public class OrderDaoTest {

    @Resource
    OrderDao orderDao;

    @Test
    public void testInsertOrder() {
        for (int i = 1; i < 20; i++) {
            orderDao.insertOrder(new BigDecimal(i), 1L, "SUCCESS");

        }
    }

    @Test
    public void testSelectOrderByIds() {
        List<Long> ids = new ArrayList<>();
        ids.add(884877870038515712L);
        ids.add(373897037306920961L);

        List<Map> maps = orderDao.selectOrderByIds(ids);
        System.out.println(maps);
    }
}

通过日志可以发现order_id为奇数的被插入到t_order_2表,为偶数的被插入到t_order_1表,达到预期目标。

注意事项

(1)网上有些教程引入的是sharding‐jdbc‐spring‐boot‐starter,而我引入的是shardingsphere-jdbc-core-spring-boot-starter,这两者的自动化配置是不一样的,要特别注意。

(2)数据源配置中的spring.shardingsphere.datasource.type属性不能为空,不然会报NullPointerException,原因如下:

org.apache.shardingsphere.spring.boot.ShardingSphereAutoConfiguration

org.apache.shardingsphere.spring.boot.datasource.DataSourceMapSetter#getDataSource

这里使用的是toString()方法,所以spring.shardingsphere.datasource.type不能为空

(3)经常出现inline表达式出错,原因就是写错了或者表达式包含了非英文字符,要小心。

其他集成方式

@Configuration
public class ShardingJdbcConfig {

    // 定义数据源
    private Map<String, DataSource> createDataSourceMap() {
        HikariDataSource dataSource1 = new HikariDataSource();
        dataSource1.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource1.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/order_db?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai");
        dataSource1.setUsername("root");
        dataSource1.setPassword("123456");
        Map<String, DataSource> result = new HashMap<>();
        result.put("m1", dataSource1);
        return result;
    }


    // 定义t_order表的分片策略
    private ShardingTableRuleConfiguration getOrderTableRuleConfiguration() {
        ShardingTableRuleConfiguration result = new ShardingTableRuleConfiguration("t_order", "m1.t_order_$->{1..2}");
        //标准的单分片策略, 两个参数:分片字段以及分片算法名称
        StandardShardingStrategyConfiguration standardShardingStrategyConfiguration = new StandardShardingStrategyConfiguration("order_id", "t_order_inline");
        result.setTableShardingStrategy(standardShardingStrategyConfiguration);
        // 定义主键生成策略
        result.setKeyGenerateStrategy(new KeyGenerateStrategyConfiguration("order_id", "snowflake"));
        return result;
    }

    // 定义分片规则
    @Bean
    DataSource getShardingDataSource() throws SQLException {
        ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
        //添加分片策略
        shardingRuleConfig.getTables().add(getOrderTableRuleConfiguration());
        //设置主键的生成策略, snowflake在上面分片策略定义了
        shardingRuleConfig.getKeyGenerators().put("snowflake", new ShardingSphereAlgorithmConfiguration("SNOWFLAKE", new Properties()));
        //分片算法属性配置, 配置是INLINE表达式
        Properties props = new Properties();
        props.setProperty("algorithm-expression", "t_order_$->{order_id % 2 + 1}");
        //t_order_inline在上面分片策略中定义了
        shardingRuleConfig.getShardingAlgorithms().put("t_order_inline", new ShardingSphereAlgorithmConfiguration("INLINE", props));
        Properties properties = new Properties();
        properties.put("sql.show", "true");
        return ShardingSphereDataSourceFactory.createDataSource(createDataSourceMap(), Arrays.asList(shardingRuleConfig), properties);
    }
}

由于采用了配置类所以需要屏蔽原来application.properties文件中spring.shardingsphere开头的配置信息。

还需要在SpringBoot启动类中屏蔽使用spring.shardingsphere配置项的类:

@SpringBootApplication(exclude = {ShardingSphereAutoConfiguration.class})

 

posted @   残城碎梦  阅读(719)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示