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})
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)