• SpringBoot3分库分表


    标签:ShardingSphere5.分库.分表;

    一、简介

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

    不得不提ShardingSphere5文档中描述的两个基本概念:

    垂直分片

    按照业务拆分的方式称为垂直分片,又称为纵向拆分,它的核心理念是专库专用。在拆分之前,一个数据库由多个数据表构成,每个表对应着不同的业务。而拆分之后,则是按照业务将表进行归类,分布到不同的数据库中,从而将压力分散至不同的数据库。

    水平分片

    水平分片又称为横向拆分。 相对于垂直分片,它不再将数据根据业务逻辑分类,而是通过某个字段(或某几个字段),根据某种规则将数据分散至多个库或表中,每个分片仅包含数据的一部分。

    下面从案例实践中,看看ShardingSphere5框架是如何实现分库分表的原理;

    二、工程搭建

    1、工程结构

    2、依赖管理

    这里只看两个核心组件的依赖:shardingsphere-jdbc组件是5.2.1版本,mybatis组件是3.5.13版本,在依赖管理中还涉及MySQL和分页等,并且需要添加很多排除配置,具体见源码;

    
    <dependency>
        <groupId>org.mybatis.spring.bootgroupId>
        <artifactId>mybatis-spring-boot-starterartifactId>
        <version>${mybatis.version}version>
    dependency>
    
    
    <dependency>
        <groupId>org.apache.shardingspheregroupId>
        <artifactId>shardingsphere-jdbc-core-spring-boot-starterartifactId>
        <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 sellerList = sellerMapper.selectByExample(sellerExample) ;
            // 3、构建分页实体对象
            PageInfo 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 orderList = orderMapper.selectByExample(orderExample) ;
            // 3、构建分页实体对象
            PageInfo 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 orderInfo (@PathVariable Integer orderId){
            Map 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
    
  • 相关阅读:
    智能运维监控系统怎么选
    [附源码]计算机毕业设计JAVA科院垃圾分类系统
    【前端异常】JavaScript错误处理:分析 Uncaught(in promise) error
    java计算机毕业设计小学生素质成长记录平台源码+系统+mysql数据库+lw文档+部署
    前端 表单标签(二)
    springboot留守儿童爱心网站springboot35
    二叉搜索树之:【中序遍历一棵二叉搜索树】【给一棵有固定形态的二叉搜索树填值】【用BST中序遍历的性质填值】【之前讲过层序遍历】
    Ruo-Yi前后端分离相关笔记
    Nginx (7):nginx高可用配置
    初入自媒体短视频行业,有什么需要注意的?怎么快速入行?
  • 原文地址:https://www.cnblogs.com/cicada-smile/p/17622090.html