Spring boot + Sharding JDBC 分库分表 及 分布式事务处理_sharding-transaction-spring-boot-starter 依赖-程序员宅基地

技术标签: spring boot  java  # spring boot 集成相关  

Sharding JDBC 基础概念

Apache ShardingSphere 是一套开源的分布式数据库解决方案组成的生态圈,它由 JDBC、Proxy 和 Sidecar(规划中)这 3 款既能够独立部署,又支持混合部署配合使用的产品组成。 它们均提供标准化的数据水平扩展、分布式事务和分布式治理等功能,可适用于如 Java 同构、异构语言、云原生等各种多样化的应用场景。

Apache ShardingSphere 旨在充分合理地在分布式的场景下利用关系型数据库的计算和存储能力,而并非实现一个全新的关系型数据库。 关系型数据库当今依然占有巨大市场份额,是企业核心系统的基石,未来也难于撼动,我们更加注重在原有基础上提供增量,而非颠覆。

Apache ShardingSphere 5.x 版本开始致力于可插拔架构,项目的功能组件能够灵活的以可插拔的方式进行扩展。 目前,数据分片、读写分离、数据加密、影子库压测等功能,以及 MySQL、PostgreSQL、SQLServer、Oracle 等 SQL 与协议的支持,均通过插件的方式织入项目。 开发者能够像使用积木一样定制属于自己的独特系统。Apache ShardingSphere 目前已提供数十个 SPI 作为系统的扩展点,仍在不断增加中。在这里插入图片描述

简单使用

引入依赖

 		<!-- sharding jdbc 依赖 -->
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
            <version>4.0.0-RC1</version>
        </dependency>

        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>sharding-core-common</artifactId>
            <version>4.0.0-RC1</version>
        </dependency>

        <!-- 分布式事务管理 -->
        <dependency>
            <groupId>io.shardingsphere</groupId>
            <artifactId>sharding-transaction-spring-boot-starter</artifactId>
            <version>3.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.4</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.21</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

编写application配置
我这边将配置文件拆开,方便演示不同策略进行分库分表。

server:
  port: 8085

spring:
  main:
    allow-bean-definition-overriding: true
  profiles:
    active: sub

mybatis:
  mapper-locations: classpath:mapper/*.xml

在处理分库分表的时候,我们可以分库分表同时进行,或者单独分库或者分表,通过设置相应的策略实现相应的功能。

spring:
  main:
    allow-bean-definition-overriding: true
  shardingsphere:
    # 参数配置 ,显示SQL
    props:
      sql:
        show: true
    # 配置数据源
    datasource:
      # 给每个数据源取别名 任意取
      names: ds0,ds1
      # 给master-ds1配置数据库连接信息
      ds0:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://192.168.26.128:3306/myslave?useSSL=false&useUnicode=true&characterEncoding=utf-8
        username: root
        password: 123456
        maxPoolSize: 100
        minPoolSize: 5
      ds1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://192.168.26.129:3306/myslave?useSSL=false&useUnicode=true&characterEncoding=utf-8
        username: root
        password: 123456
        maxPoolSize: 100
        minPoolSize: 5
    #配置默认数据源ds0
    sharding:
      # 默认数据源,主要用于写,注意一定要配置;如果不配置就会把三个节点都当作从slave节点,新增,修改和删除会出错。
      default-data-source-name: ds0
      # 配置分表规则 --》单数存储在user1中,偶数存放在user0中
      tables:
        # 逻辑表名 user
        user:
          # 数据节点 : 数据源${
    0..N}.逻辑表名${
    0..N},
          # ds0.user0 代表ds0数据中的user0表
          actual-data-nodes: ds${
    0..1}.user${
    0..1}
          # 只分表不分库
          # actual-data-nodes: ds0.user${
    0..1}
          # 拆分库策略 将数据存放到指定库中
          database-strategy:
            inline:
              sharding-column: age  # 分片字段
              algorithm-expression: ds${
    age % 2}  # 分片算法表达式
          table-strategy:
            inline:
              sharding-column: age  # 分片字段
              algorithm-expression: user${
    age % 2}  # 分片算法表达式

创建数据库
由上文的配置可知,我们需要在两台虚拟机中分表创建两张user表,分别是user0,user1;如果不创建的话会导致我们插入数据失败。
在这里插入图片描述
编写业务代码
实体类

@Data
public class User {
    
    private Long id;

    private String nickname;

    private String password;

    private Integer age;

    private Integer sex;

    private Date birthday;
}

Mapper

@Mapper
public interface UserMapper {
    

    @Insert("insert into user(nickname,password,age,sex,birthday) values(#{nickname},#{password},#{age},#{sex},#{birthday})")
    void addUser(User user);

    @Select("select * from user")
    List<User> findUsers();
}

测试

	@Autowired
    private UserMapper userMapper;
    
    @Test
    void contextLoads() {
    
        User user = new User();
        user.setNickname("张三" + new Random().nextInt());
        user.setPassword("123");
        user.setAge(22);
        user.setSex(22);
        user.setBirthday(new Date());
        userMapper.addUser(user);
    }

可以看到将数据插入到ds0数据库中的user0表中。
在这里插入图片描述
将测试代码中的年龄修改为单数后,可以发现数据插入到ds1数据库中的user1表中。
在这里插入图片描述

使用standard策略进行分片

该案例根据上述用户表中的birthday字段进行分片。
编写application配置

spring:
  main:
    allow-bean-definition-overriding: true
  shardingsphere:
    # 参数配置 ,显示SQL
    props:
      sql:
        show: true
    # 配置数据源
    datasource:
      # 给每个数据源取别名 任意取
      names: ds0,ds1
      # 给master-ds1配置数据库连接信息
      ds0:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://192.168.26.128:3306/myslave?useSSL=false&useUnicode=true&characterEncoding=utf-8
        username: root
        password: 123456
        maxPoolSize: 100
        minPoolSize: 5
      ds1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://192.168.26.129:3306/myslave?useSSL=false&useUnicode=true&characterEncoding=utf-8
        username: root
        password: 123456
        maxPoolSize: 100
        minPoolSize: 5
    #配置默认数据源ds0
    sharding:
      # 默认数据源,主要用于写,注意一定要配置;如果不配置就会把三个节点都当作从slave节点,新增,修改和删除会出错。
      default-data-source-name: ds0
      # 配置分表规则 --》单数存储在user1中,偶数存放在user0中
      tables:
        # 逻辑表名 user
        user:
          actual-data-nodes: ds${
    0..1}.user${
    0..1}
          # 拆分库策略 将数据存放到指定库中
          database-strategy:
            standard:
              shardingColumn: birthday
              preciseAlgorithmClassName: com.example.springbootshardingjdbc.algorithm.BirthdayAlgorithm
          table-strategy:
            inline:
              sharding-column: age  # 分片字段
              algorithm-expression: user${
    age % 2}  # 分片算法表达式

编写策略配置文件
datalist的数量要根据你定义的数据库数量来,否则会出错。

public class BirthdayAlgorithm implements PreciseShardingAlgorithm<Date> {
    

    List<Date> dateList = new ArrayList<>();

    {
    
        Calendar calendar1 = Calendar.getInstance();
        calendar1.set(2021, 1, 1, 0, 0, 0);
        Calendar calendar2 = Calendar.getInstance();
        calendar2.set(2022, 1, 1, 0, 0, 0);
        dateList.add(calendar1.getTime());
        dateList.add(calendar2.getTime());
    }

    @Override
    public String doSharding(Collection<String> collection, PreciseShardingValue<Date> preciseShardingValue) {
    
        // 获取属性数据库的值
        Date date = preciseShardingValue.getValue();
        // 获取数据源的名称消息列表
        Iterator<String> iterable = collection.iterator();
        String target = null;
        for (Date date1 : dateList) {
    
            target = iterable.next();
            //如果数据晚于指定日期
            if (date.before(date1)) {
    
                break;
            }
        }
        return target;
    }
}

测试

 	@Test
    void contextLoads() {
    
        User user = new User();
        user.setNickname("张三" + new Random().nextInt());
        user.setPassword("123");
        user.setAge(1);
        user.setSex(22);
        user.setBirthday(new Date());
        userMapper.addUser(user);
    }

由于当前时间晚于datalist时间,所以直接返回第一个数据库,将数据插入到ds0数据库中。
在这里插入图片描述
在这里插入图片描述

插入数据时主键使用雪花算法

只需要在application中进行配置即可;配置雪花算法之后,我们需要将数据库中主键的类型改为bigint型。

 #配置默认数据源ds1
    sharding:
      # 默认数据源,主要用于写,注意一定要配置读写分离;如果不配置就会把三个节点都当作从slave节点,新增,修改和删除会出错。
      default-data-source-name: ds0
      # 配置分表规则 --》单数存储在user1中,偶数存放在user0中
      tables:
        # 逻辑表名 user
        user:
          key-generator:
            # 主键 雪花算法
            column: id
            type: SNOWFLAKE
          # 只分表不分库
          actual-data-nodes: ds0.user${
    0..1}
          table-strategy:
            inline:
              sharding-column: age  # 分片字段
              algorithm-expression: user${
    age % 2}  # 分片算法表达式

按照日期将数据插入到对应的表中

我们可以将数据按照日期分别存放到对应的表中,比如订单数据,我们可以将其区分为1月的订单,2月的订单等。这里采用最简单的方法,直接通过字段进行匹配存放。
创建数据库
这边只演示分表,没进行分库,所以只需要在本地数据库创建3张order表即可,分别是order_202101,order_202102,order_202103。
在这里插入图片描述
编写application配置

spring:
  main:
    allow-bean-definition-overriding: true
  shardingsphere:
    # 参数配置 ,显示SQL
    props:
      sql:
        show: true
    # 配置数据源
    datasource:
      # 给每个数据源取别名 任意取
      names: ds0,ds1
      # 给master-ds1配置数据库连接信息
      ds0:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://192.168.26.128:3306/myslave?useSSL=false&useUnicode=true&characterEncoding=utf-8
        username: root
        password: 123456
        maxPoolSize: 100
        minPoolSize: 5
      ds1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://192.168.26.129:3306/myslave?useSSL=false&useUnicode=true&characterEncoding=utf-8
        username: root
        password: 123456
        maxPoolSize: 100
        minPoolSize: 5
    #配置默认数据源ds1
    sharding:
      # 默认数据源,主要用于写,注意一定要配置读写分离;如果不配置就会把三个节点都当作从slave节点,新增,修改和删除会出错。
      default-data-source-name: ds0
      # 配置分表规则 --》单数存储在user1中,偶数存放在user0中
      tables:
        # 逻辑表名 user
        order:
          key-generator:
            # 主键 雪花算法需要修改
            column: id
            type: SNOWFLAKE
          # collect 的作用是补0 order_2021_01
          actual-data-nodes: ds0.order_2021${
    (1..3).collect{
    t->t.toString().padLeft(2,'0')}}
          table-strategy:
            inline:
              sharding-column: yearmonth  # 分片字段
              algorithm-expression: order_${
    yearmonth}  # 分片算法表达式

编写业务代码
实体类

@Data
public class Order {
    

    private Long id;

    private String ordernumber;

    private Long userid;

    private String yearmonth;

    private Date createTime;
}

mapper
由于我们使用yearmonth进行分片,所以我们插入数据时必须带上该属性,如果不带的话则会插入到所有表中。

@Mapper
public interface OrderMapper {
    

    @Insert("insert into order(ordernumber,userid,create_time,yearmonth) values(#{ordernumber},#{userid},#{createTime},#{yearmonth})")
    void addUserOrder(Order order);

    @Select("select count(1) from order")
    Integer count();

}

测试
可以看到将yearmonth设置为202102后,将该条数据插入到order_202102表中。

	@Test
    void order() {
    
        Order order = new Order();
        order.setUserid(1L);
        order.setOrdernumber("123");
        order.setCreateTime(new Date());
        order.setYearmonth("202102");
        orderMapper.addUserOrder(order);
    }

在这里插入图片描述

分布式事务处理

在处理事务的时候,如果发生错误,我们需要进行数据的回滚,确保数据的一致性。sharding jdbc 也提供了相对应的方法。
首先需要添加相关依赖

	<dependency>
       <groupId>io.shardingsphere</groupId>
       <artifactId>sharding-transaction-spring-boot-starter</artifactId>
       <version>3.1.0</version>
    </dependency>
     // 分布式事务回滚
    @ShardingTransactionType(TransactionType.XA)
    //单库 事务回滚
    @Transactional(rollbackFor = Exception.class)
    public int saveUserOrder(User user, Order order) {
    
        userMapper.addUser(user);

        //int a = 1 / 0;

        orderMapper.addUserOrder(order);
        return 1;
    }

如果是单数据库,我们可以使用Transactional注解进行事务的回滚;
如果是微服务架构,我们则可以使用ShardingTransactionType注解处理;

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_41432730/article/details/120434541

智能推荐

hdoj Be the Winner 2509 (NIM博弈)_hduojbe the winner-程序员宅基地

文章浏览阅读326次。Be the WinnerTime Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 2713 Accepted Submission(s): 1484Problem DescriptionLet's consider m_hduojbe the winner

unity 手游虚拟摇杆代码参考-程序员宅基地

文章浏览阅读3.2k次。手游 摇杆_摇杆代码

memlock过低导致的数据库性能问题(r6笔记第10天)-程序员宅基地

文章浏览阅读824次。今天在一台备库机器上准备搭建active data guard ,在主库上做配置的时候,发现主库的反应有些慢,主要的感觉就是敲命令的时候似乎都有些停顿。带着疑问查看了下数..._memlock limit is less than 64mb

推荐几本shell学习的书 (r9笔记第98天)-程序员宅基地

文章浏览阅读180次。周末整理了一下书架,一来书架上实在是放不下东西了,四层书架,两层在闺女的触及范围之内,所以直接拿胶带封住,留下两层勉强可用。二来书架已经不是放书的地儿,生活用品已..._shell 书籍 推荐 杨建荣

PE文件格式_pe工具的文件扩展名-程序员宅基地

文章浏览阅读1.2k次。PE文件格式分析及修改(图)12009-01-09 14:08PE 的意思是 Portable Executable(可移植的执行体)。它是 Win32环境自身所带的执行文件格式。它的一些特性继承自Unix的Coff(common object file format)文件格式。“Portable Executable”(可移植的执行体)意味着此文件格式是跨Win32平台的_pe工具的文件扩展名

Bluetooth MESH探究 --- (7) BLE core spec之为什么BLE能有更低功耗_ble 为什么功耗低-程序员宅基地

文章浏览阅读3.6k次。BLE与其它蓝牙协议最典型的区别就是BLE是专门为低功耗、低复杂度以及低成本设备设计。那么,BLE是通过什么方法做到更多功耗的呢? 对于蓝牙设备甚至可以说对于所有无线通信设备来说,最大的功耗就来自于射频电路部分。比如,对于TI CC2540芯片来说,RF处于接收状态的电流为19.6mA,RF处于发射状态的电流为24mA,而RF处于sleep状态的电流仅为0.9uA。所以,如果能够最大限度地_ble 为什么功耗低

随便推点

纳什博弈论-程序员宅基地

文章浏览阅读1.5k次。纳什博弈论的原理与应用   1950年和1951年纳什的两篇关于非合作博弈论的重要论文,彻底改变了人们对竞争和市场的看法。他证明了非合作博弈及其均衡解,并证明了均衡解的存在性,即著名的纳什均衡。从而揭示了博弈均衡与经济均衡的内在联系。纳什的研究奠定了现代非合作博弈论的基石,后来的博弈论研究基本上都沿着这条主线展开的。然而,纳什天才的发现却遭到冯·诺依曼的断然否定,在此之前他还受到爱因斯坦的冷遇。但是骨子里挑战权威、藐视权威的本性,使纳什坚持了自己的观点,终成一代大师。要不是30多年的严重精神病折_纳什博弈论

ios注意事项-程序员宅基地

文章浏览阅读177次。1.tableview顶部留白问题当cell的类型是plaint类型时,直接设置self.automaticallyAdjustsScrollViewInsets=NO;应该就可以的当cell的类型是group类型时,此时要去掉tableView顶部的空白需要两步:1.设置tableView的tableHeaderView高度为0.5;self.MenuTable.tableHea

数据结构——不带头节点的单链表_不带图节点的单链表类的成员函数-程序员宅基地

文章浏览阅读403次。作者:小琛欢迎转载,请标明出处单链表的概念概念:**链表是一种物理存储结构上非连续、非顺序,逻辑上连续的结构。**是通过链表中的指针链 接次序实现的。如下图:单链表的实现鉴于后续的面试和oj题目类型,这里实现不带头节点的单链表Plist.h#ifndef _LIST_H__#define _LIST_H__#include <stdio.h>#include &l..._不带图节点的单链表类的成员函数

Cento7配置网络及代理_centos7网卡不支持代理-程序员宅基地

文章浏览阅读528次。1、配置网络编辑网卡配置文件[root@localhost yum.repos.d]# vim /etc/sysconfig/network-scripts/ifcfg-eno16777736TYPE=EthernetBOOTPROTO=noneDEFROUTE=yesIPV4_FAILURE_FATAL=noIPV6INIT=yesIPV6_AUTOCONF=yesIPV6_..._centos7网卡不支持代理

input输入框赋值、取值_input框赋值取值-程序员宅基地

文章浏览阅读3.9k次。目录htmleasyuilayui自己做个记录,在对页面进行修改,开发的时候,总是去查资料html<input id="cpno" name="cpno" value="" />js: 赋值: $('#cpno').val( "CSDN有限公司"); -------方式1 $('#cpno').val("setValue", "CSDN有限公司"); -------方式..._input框赋值取值

如何有效预防CC攻击?_怎么防止被c&c攻击-程序员宅基地

文章浏览阅读348次。CC攻击也是属于流量型攻击中的一种,大部分的高防机房,是可以承受住大量的DDOS攻击的,这些防护主要来源于硬防。CC攻击可以算的上是DDOS攻击的一个升级版,如果想要抵御CC攻击可没有抵御DDOS攻击那么简单,单靠硬防还是不够的,必须要配合人工进行策略调控才可以防御住。CC攻击基本上都是针对端口的攻击,所以网站遇到CC攻击的几率会更大一些。在攻击模式上与一般的流量型攻击是差不多的,主要是通过伪装成正常的用户不停的对网站进行访问,从而导致服务器资源耗尽,无法正常的被访问。那么我们应该怎么去预防CC攻击呢?_怎么防止被c&c攻击

推荐文章

热门文章

相关标签