写点什么

SpringBoot3 分库分表

作者:知了一笑
  • 2023-08-11
    浙江
  • 本文字数:3910 字

    阅读完需:约 13 分钟

SpringBoot3分库分表

标签:ShardingSphere5.分库.分表;

一、简介

分库分表的设计和实现方式,在之前的内容中总结过很多,本文基于SpringBoot3ShardingSphere5框架实现数据分库分表的能力;


不得不提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_0shard_db_1tb_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 索引结构,如果没有搜索的需求,可以在订单表分页查询后去拼接其他结构;


@RestControllerpublic 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
复制代码


发布于: 刚刚阅读数: 3
用户头像

知了一笑

关注

公众号:知了一笑 2020-04-08 加入

源码仓库:https://gitee.com/cicadasmile

评论

发布
暂无评论
SpringBoot3分库分表_Java_知了一笑_InfoQ写作社区