SpringBoot3分库分表 - 知了一笑
source link: https://www.cnblogs.com/cicada-smile/p/17622090.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
标签:ShardingSphere5.分库.分表;
分库分表的设计和实现方式,在之前的内容中总结过很多,本文基于SpringBoot3
和ShardingSphere5
框架实现数据分库分表的能力;
不得不提ShardingSphere5
文档中描述的两个基本概念:
垂直分片
按照业务拆分的方式称为垂直分片,又称为纵向拆分,它的核心理念是专库专用。在拆分之前,一个数据库由多个数据表构成,每个表对应着不同的业务。而拆分之后,则是按照业务将表进行归类,分布到不同的数据库中,从而将压力分散至不同的数据库。
水平分片
水平分片又称为横向拆分。 相对于垂直分片,它不再将数据根据业务逻辑分类,而是通过某个字段(或某几个字段),根据某种规则将数据分散至多个库或表中,每个分片仅包含数据的一部分。
下面从案例实践中,看看ShardingSphere5
框架是如何实现分库分表的原理;
二、工程搭建
1、工程结构
2、依赖管理
这里只看两个核心组件的依赖:shardingsphere-jdbc
组件是5.2.1
版本,mybatis
组件是3.5.13
版本,在依赖管理中还涉及MySQL和分页等,并且需要添加很多排除配置,具体见源码;
<!-- Mybatis组件 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!-- ShardingSphere分库分表 -->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>${shardingsphere.version}</version>
</dependency>
三、配置详解
1、配置文件
此处只展示分库分表的相关配值,默认数据源使用db_master
库,注意tb_order
库表路由的策略和分片算法的关联关系,其他工程配置详见源码仓库;
spring:
# 分库分表配置
shardingsphere:
datasource:
# 默认数据源
sharding:
default-data-source-name: db_master
names: db_master,db_0,db_1
db_master:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/shard_db
username: root
password: 123456
db_0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/shard_db_0
username: root
password: 123456
db_1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/shard_db_1
username: root
password: 123456
rules:
sharding:
tables:
# tb_order逻辑
tb_order:
actual-data-nodes: db_${0..1}.tb_order_${0..2}
# tb_order库路由
database-strategy:
standard:
sharding-column: order_id
sharding-algorithm-name: database_inline
# tb_order表路由
table-strategy:
standard:
sharding-column: order_id
sharding-algorithm-name: table_inline
sharding-algorithms:
# tb_order库路由算法
database_inline:
type: INLINE
props:
algorithm-expression: db_${order_id % 2}
# tb_order表路由算法
table_inline:
type: INLINE
props:
algorithm-expression: tb_order_${order_id % 3}
props:
sql-show: true
sql-comment-parse-enabled: true
2、配置原理
在配置中需要管理三个数据源,shard_db
默认库,在操作不涉及需要路由的表时默认使用该数据源,shard_db_0
和shard_db_1
是tb_order
逻辑表的路由库;
逻辑表tb_order
整体使用两个数据库,每个库建3张结构相同相同的表,在操作tb_order
数据时,会根据order_id
字段值定位数据所属的分片节点;
- 库路由
db_${0..1}
采用db_${order_id%2}
的算法; - 表路由
tb_order_${0..2}
采用tb_order_${order_id%3}
的算法;
四、测试案例
1、主库操作
基于Mybatis持久层框架,实现对shard_db
默认库的数据操作,注意控制台的日志打印,可以看到一系列解析逻辑以及库表节点的定位,分页查询使用PageHelper组件即可;
public class MasterTest {
@Autowired
private BuyerMapper buyerMapper ;
@Autowired
private SellerMapper sellerMapper ;
@Test
public void testBuyerQuery (){
// 主键查询
Buyer buyer = buyerMapper.selectByPrimaryKey(1) ;
System.out.println(buyer.getId()+";"+buyer.getBuyerName());
}
@Test
public void testBuyerInsert (){
// 新增数据
Buyer buyer = new Buyer() ;
buyer.setBuyerName("买家Three");
System.out.println(buyerMapper.insert(buyer));
}
@Test
public void testBuyerUpdate (){
// 更新数据
Buyer buyer = buyerMapper.selectByPrimaryKey(3) ;
if (buyer != null){
buyer.setBuyerName("Three买家");
System.out.println(buyerMapper.updateByPrimaryKey(buyer));
}
}
@Test
public void testSellerPage (){
// 1、设置分页和查询条件
PageHelper.startPage(2,2) ;
SellerExample sellerExample = new SellerExample() ;
sellerExample.setOrderByClause("id asc");
// 2、查询数据
List<Seller> sellerList = sellerMapper.selectByExample(sellerExample) ;
// 3、构建分页实体对象
PageInfo<Seller> pageInfo = new PageInfo<>(sellerList) ;
System.out.println(pageInfo);
}
}
2、分库操作
在对tb_order
表执行增删改查时,会根据order_id
的字段值计算库表的路由节点,注意分页时会查询所有的分库和分表,然后汇总查询的结果;
public class ShardTest {
@Autowired
private OrderMapper orderMapper ;
/**
* 写入100条数据
*/
@Test
public void testOrderInsert (){
for (int i=1 ; i<= 100 ; i++){
Order order = new Order(i,i%3+1,i%3+1) ;
// orderMapper.insert(order) ;
}
}
@Test
public void testOrderQuery (){
Order order = orderMapper.selectByPrimaryKey(5) ;
System.out.println(order);
}
@Test
public void testOrderUpdate (){
Order order = orderMapper.selectByPrimaryKey(100) ;
if (order != null){
// 原数据:买家和卖家ID都是2
order.setBuyerId(1);
order.setSellerId(3);
orderMapper.updateByPrimaryKey(order) ;
}
}
@Test
public void testOrderPage (){
// 1、设置分页和查询条件
PageHelper.startPage(1,10) ;
OrderExample orderExample = new OrderExample() ;
orderExample.createCriteria().andBuyerIdEqualTo(2).andSellerIdEqualTo(2);
orderExample.setOrderByClause("order_id desc");
// 2、查询数据
List<Order> orderList = orderMapper.selectByExample(orderExample) ;
// 3、构建分页实体对象
PageInfo<Order> pageInfo = new PageInfo<>(orderList) ;
System.out.println(pageInfo);
}
}
3、综合查询
编写一个订单详情查询接口,同时使用三个库构建数据结构;如果是基于列表数据的检索,比较常规做法的是构建ES索引结构,如果没有搜索的需求,可以在订单表分页查询后去拼接其他结构;
@RestController
public class OrderController {
@Resource
private BuyerMapper buyerMapper ;
@Resource
private SellerMapper sellerMapper ;
@Resource
private OrderMapper orderMapper ;
/**
* 查询订单详情
*/
@GetMapping("/order/info/{orderId}")
public Map<String,Object> orderInfo (@PathVariable Integer orderId){
Map<String,Object> orderMap = new HashMap<>() ;
Order order = orderMapper.selectByPrimaryKey(orderId) ;
if (order != null){
orderMap.put("order",order) ;
orderMap.put("buyer",buyerMapper.selectByPrimaryKey(order.getBuyerId())) ;
orderMap.put("seller",sellerMapper.selectByPrimaryKey(order.getSellerId())) ;
}
return orderMap ;
}
}
查看SQL语句
db_master ::: select id, buyer_name from tb_buyer where id = ? ::: [1]
db_master ::: select id, seller_name from tb_seller where id = ? ::: [3]
db_0 ::: select order_id, seller_id, buyer_id from tb_order_1 where order_id = ? ::: [100]
五、参考源码
文档仓库:
https://gitee.com/cicadasmile/butte-java-note
源码仓库:
https://gitee.com/cicadasmile/butte-spring-parent
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK