目录
- 版本介绍
- 背景介绍
- 拆分方式
- 集成并测试
- 1.引入依赖
- 2.创建库和表
- 3.pom文件配置
- 3.编写测试类
- Entity层
- Mapper接口
- MapperXML文件
- 测试类
- 4.运行结果
- 自定义分片规则
- 定义分片类
- 编写pom文件
- 总结提升
版本介绍
SpringBoot的版本是: 2.3.12
ShardingSphere-jdbc的版本是:5.1.2
Mysql-Connector的版本是:8.0.19
Druid的版本是:1.2.8
背景介绍
分库分表是数据库设计中的一种常见策略,主要用于解决在高并发、大数据量场景下的性能瓶颈和管理问题。本文章采用Sharding-JDBC以水平分配的方式来实现分库分表。
ShardingSphere官网:https://shardingsphere.apache.org/document/5.1.2/cn/overview/
分库分表的原因:
- 性能提升:单一数据库在高并发情况下容易发生性能瓶颈,分库分表可以将请求分散到多个数据库或表上,降低单个数据库的压力。
- 容量管理:随着数据量的增长,单个数据库的存储能力可能不足。分库分表可以将数据分散到多个数据库中,以支撑更大的数据存储需求。
- 效率提升:在查询时,分表可以将查询范围缩小,只需要访问更少的数据,从而提高查询效率。
- 可维护性:分库分表可以更方便地进行数据备份、恢复和维护,提高系统的整体可维护性。
- 横向扩展:分库分表支持横向扩展,允许在现有架构中增加更多的数据库服务器,以适应业务增长。
拆分方式
1. 分库
垂直分库:将不同功能模块或业务逻辑的数据存储在不同的数据库中。例如,用户数据和订单数据分别存放在不同的数据库中。
水平分库:类似于分表,但是将同一类型的数据分散到不同的数据库中。例如,将用户数据按ID范围分散到不同的数据库。
2. 分表
按范围分表:根据某个字段(如时间、ID等)的范围,将数据划分到不同的表中,每个表存储特定范围的数据。
按哈希分表:对某个字段(如用户ID)进行哈希运算,根据哈希值将数据分配到不同的表中。
集成并测试
1.引入依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>
<!-- Database Driver -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.1.2</version>
</dependency>
2.创建库和表
在每个数据库中都需要创建多个相同的表,例如order_info_0、order_info_2、order_info_3等等,这样在配置分片规则的时候才会根据不同的计算方式映射到不同的库和表里面。
3.pom文件配置
server:
port: 8081
spring:
autoconfigure: # 排除druid 否则报错
exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
shardingsphere:
# 开启sql打印
enabled: true
props:
# 是否显示sql
sql-show: true
datasource:
#数据源名称
names: sharding0,sharding1
#数据源实例: 如果这里还有master
sharding0:
type: com.alibaba.druid.pool.DruidDataSource
driver-class: com.mysql.cj.jdbc.Driver
#使用Druid,不能使用jdbc-url 得使用url
url: jdbc:mysql://localhost:3307/budget-lim-dev?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: 123456
sharding1:
type: com.alibaba.druid.pool.DruidDataSource
driver-class: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/budget-lim-dev?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: 123456
#分片规则
rules:
sharding:
#对表进行分片
tables:
#逻辑表名,代表的是需要分表的名称
order_info:
#实际节点:这里代表的是 会使用sharding数据源中 order_info表 细分为0~3 4个表
actual-data-nodes: sharding$->{0..1}.order_info_$->{0..1}
#库策略
database-strategy:
standard:
#根据num来进行分库
sharding-column: num
sharding-algorithm-name: alg_round-robin
#表策略
table-strategy:
#标准表策略
standard:
#分表的列
sharding-column: id
#分片算法名称: 来源于下面的sharding-algorithms
sharding-algorithm-name: alg_random
key-generate-strategy: # 主键生成策略
column: id # 主键列
key-generator-name: snowflake # 策略算法名称(推荐使用雪花算法)
#主键生成规则,SNOWFLAKE 雪花算法
key-generators:
snowflake:
type: SNOWFLAKE
#分片算法
sharding-algorithms:
alg_hash_mod:
#类型:hash取余 类似于获取一个列的数
type: HASH_MOD
#分片的数量,因为是2个表,所以是2
props:
sharding-count: 2
mybatis:
#映射文件 配置之后,mybatis会去扫描该路径下的xml文件,才会与Mapper对应起来
mapper-locations: classpath:mapper/*.xml
#别名类(实体类)所在包
type-aliases-package: com.wzil.simplesharding.entity
configuration:
#驼峰转换
map-underscore-to-camel-case: true
3.编写测试类
Entity层
/**
* @Author: wzil
* @DATE: 2024年7月24日
* @Description:
**/
@Data
public class OrderInfo {
private Long id;
private String name;
private Integer num;
private Date createTime;
}
Mapper接口
/**
* @Author: wzil
* @DATE: 2024年7月24日15:26:30
* @Description: 创建mapper接口,
**/
@Mapper
public interface OrderInfoMapper {
List<OrderInfo> list();
void save(OrderInfo orderInfo);
void deleteById(@Param("id") Long id);
void updateNameById(@Param("id") Long id, @Param("name") String name);
OrderInfo getById(@Param("id") Long id);
List<OrderInfo> limitOrder();
List<OrderInfo> limitOrderWithOffset();
}
MapperXML文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace:命名空间,用来映射对应的mapper
相当于将mapper和mapper.xml连接起来,这一步很重要-->
<mapper namespace="com.wzil.simplesharding.mapper.OrderInfoMapper">
<insert id="save">
insert into order_info(`name`,num,create_time) values(#{name},#{num},#{createTime})
</insert>
<update id="updateNameById">
update order_info set name=#{name}
where id=#{id}
</update>
<delete id="deleteById">
delete from order_info where id=#{id}
</delete>
<select id="list" resultType="com.wzil.simplesharding.entity.OrderInfo">
select * from order_info
</select>
<select id="getById" resultType="com.wzil.simplesharding.entity.OrderInfo">
select * from order_info
where id=#{id}
</select>
<select id="limitOrder" resultType="com.wzil.simplesharding.entity.OrderInfo">
select * from order_info
order by num desc
limit 10
</select>
<select id="limitOrderWithOffset" resultType="com.wzil.simplesharding.entity.OrderInfo">
select * from order_info
order by num desc
limit 2,10
</select>
</mapper>
测试类
@Slf4j
@SpringBootTest
class SimpleShardingApplicationTests {
@Autowired
private OrderInfoMapper orderInfoMapper;
@Test
void add() {
for (int i = 0; i < 100; i++) {
OrderInfo orderInfo = new OrderInfo();
orderInfo.setName("wzil"+i);
orderInfo.setCreateTime(new Date());
orderInfo.setNum(i);
orderInfoMapper.save(orderInfo);
}
}
@Test
void delete() {
Long id=1022869460031111169L;
orderInfoMapper.deleteById(id);
}
@Test
void update() {
Long id=1022869460031111169L;
String name="hello";
orderInfoMapper.updateNameById(id,name);
}
@Test
void list() {
List<OrderInfo> list = orderInfoMapper.list();
System.out.println(list);
}
@Test
void getById() {
Long id=1022869460031111169L;
OrderInfo info=orderInfoMapper.getById(id);
System.out.println(info);
}
}
4.运行结果
此实例运行的是更新的语句,可以看到是对sharding1数据库中的order_info_0进行更新操作的。
自定义分片规则
定义分片类
package com.wzil.simplesharding.config;
import org.apache.shardingsphere.sharding.api.sharding.standard.PreciseShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.RangeShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.StandardShardingAlgorithm;
import java.util.Collection;
import java.util.Properties;
/**
* @BelongsProject: simple_sharding
* @BelongsPackage: com.wzil.simplesharding.config
* @Author: Wuzilong
* @Description: 轮询分片算法
* @CreateTime: 2024-07-24 14:21
* @Version: 1.0
*/
public class RoundRobinShardingAlgorithm implements StandardShardingAlgorithm<String> {
int currentIndex=0;
@Override
public String doSharding(Collection<String> collection, PreciseShardingValue<String> preciseShardingValue) {
// 计算当前的索引位置
currentIndex = (currentIndex + 1) % collection.size();
// 根据当前索引返回数据源名称
return (String) collection.toArray()[currentIndex];
}
@Override
public Collection<String> doSharding(Collection<String> collection, RangeShardingValue<String> rangeShardingValue) {
return null;
}
@Override
public Properties getProps() {
return null;
}
@Override
public void init(Properties properties) {
}
}
编写一个分片规则的类去实现StandardShardingAlgorithm接口,去重写doSharding方法。doSharding(Collection collection, PreciseShardingValue preciseShardingValue)这个方法是确定值的分片规则。 doSharding(Collection collection, RangeShardingValue rangeShardingValue)这个方法是范围值的分片规则。根据业务的需求来重写不同的方法。
编写pom文件
#分片算法
sharding-algorithms:
alg_round-robin:
#指定了算法的类型
type: CLASS_BASED
props:
#标准的分片策略
strategy: standard
#算法类的全限定名
algorithmClassName: com.walker.simplesharding.config.RoundRobinShardingAlgorithm
编写完之后就可以在上面配置的分库或者分表的规则去指定我们自己定义的alg_round-robin这个类型了。
总结提升
分库分表是一种有效的数据库设计策略,能够应对高并发和大数据量的挑战。尽管其带来了复杂性和维护成本,但在许多实际应用中,以提升性能和可扩展性为目标的分库分表依然是一个广泛采用的解决方案。在实施之前,需要深入分析业务需求和技术架构,合理设计分库分表策略,以便最大化其效益。