本文最后更新于 2024-04-18,欢迎来到我的Blog! https://www.zpeng.site/

Springboot整合Shardingjdbc实现读写分离

1、pom.xml

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.9</version>
        </dependency>
        
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
            <version>4.1.1</version>
        </dependency>

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


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

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

2、application.yml

server:
  port: 8080

spring:
  application:
    name: readwritesplitting
  main:
    allow-bean-definition-overriding: true
  shardingsphere:
    # 参数配置,显示sql
    props:
      sql:
        show: true
    # 配置数据源
    datasource:
      # 给每个数据源取别名,下面的ds1,ds2任意取名字
      names: master, slave0, slave1
      # 给master-ds1每个数据源配置数据库连接信息
      master:
        # 配置druid数据源
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/master_db?useUnicode=true&characterEncoding=utf-8
        username: root
        password: root
        maxPoolSize: 100
        minPoolSize: 5
      # 配置ds2-slave
      slave0:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/slave0_db?useUnicode=true&characterEncoding=utf-8
        username: root
        password: root
        maxPoolSize: 100
        minPoolSize: 5
      # 配置ds3-slave
      slave1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/slave1_db?useUnicode=true&characterEncoding=utf-8
        username: root
        password: root
        maxPoolSize: 100
        minPoolSize: 5
    # 配置默认数据源ds1
    sharding:
      # 默认数据源,主要用于写,注意一定要配置读写分离 ,注意:如果不配置,那么就会把三个节点都当做从slave节点,新增,修改和删除会出错。
      default-data-source-name: master
    # 配置数据源的读写分离,但是数据库一定要做主从复制
    masterslave:
      # 配置主从名称,可以任意取名字
      name: ms
      # 配置主库master,负责数据的写入
      master-data-source-name: master
      # 配置从库slave节点
      slave-data-source-names: slave0,slave1
      # 配置slave节点的负载均衡均衡策略,采用轮询机制
      load-balance-algorithm-type: round_robin


#mybatis配置信息
mybatis:
  # xml映射文件所在的路径,一般用模糊匹配来指定最终的xml文件
  mapper-locations: classpath:/mapper/*.xml
  configuration:
    #采用驼峰形式将数据表中以‘_’分隔的字段映射到java类的某个属性,比如表字段user_name可以映射为类里面的userName属性
    map-underscore-to-camel-case: true

    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

3、sql

CREATE TABLE `sys_user` (
  `user_id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID',
  `dept_id` bigint DEFAULT NULL COMMENT '部门ID',
  `user_name` varchar(30) NOT NULL COMMENT '用户账号',
  `nick_name` varchar(30) NOT NULL COMMENT '用户昵称',
  `user_type` varchar(2) DEFAULT '00' COMMENT '用户类型(00系统用户)',
  `email` varchar(50) DEFAULT '' COMMENT '用户邮箱',
  `phonenumber` varchar(11) DEFAULT '' COMMENT '手机号码',
  `sex` char(1) DEFAULT '0' COMMENT '用户性别(0男 1女 2未知)',
  `avatar` varchar(100) DEFAULT '' COMMENT '头像地址',
  `password` varchar(100) DEFAULT '' COMMENT '密码',
  `status` char(1) DEFAULT '0' COMMENT '帐号状态(0正常 1停用)',
  `del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)',
  `login_ip` varchar(128) DEFAULT '' COMMENT '最后登录IP',
  `login_date` datetime DEFAULT NULL COMMENT '最后登录时间',
  `create_by` varchar(64) DEFAULT '' COMMENT '创建者',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_by` varchar(64) DEFAULT '' COMMENT '更新者',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  `remark` varchar(500) DEFAULT NULL COMMENT '备注',
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=103 DEFAULT CHARSET=utf8mb3 COMMENT='用户信息表';

4、code

SysUser

package com.example.readwritesplitting.demo;

import lombok.Data;

import java.util.Date;

/**
 * @ClassName SysUser
 * @Description:
 * @Author: zpeng
 * @CreateDate: 2024/4/17 11:56
 */
@Data
public class SysUser {
    private Long userId;
    private Long deptId;
    private String userName;
    private String nickName;
    private String userType;
    private String email;
    private String phonenumber;
    private String sex;
    private String avatar;
    private String password;
    private String status;
    private String delFlag;
    private String loginIp;
    private Date loginDate;
    private String createBy;
    private Date createTime;
    private String updateBy;
    private Date updateTime;
    private String remark;

    // Getters and Setters

    // Optionally, add constructors, validation annotations, and/or toString() method

}

SysUserController

package com.example.readwritesplitting.demo;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * @ClassName SysUserController
 * @Description:
 * @Author: zpeng
 * @CreateDate: 2024/4/17 12:01
 */

@RestController
@RequestMapping("/api/sys/users")
public class SysUserController {
    private final SysUserService sysUserService;

    @Autowired
    public SysUserController(SysUserService sysUserService) {
        this.sysUserService = sysUserService;
    }

    @PostMapping("/add")
    public int insert(@RequestBody SysUser sysUser) {
        return sysUserService.insert(sysUser);
    }

    @DeleteMapping("/delete/{userId}")
    public int deleteByPrimaryKey(@PathVariable("userId") Long userId) {
        return sysUserService.deleteByPrimaryKey(userId);
    }

    @PutMapping("/update")
    public int updateByPrimaryKeySelective(@RequestBody SysUser sysUser) {
        return sysUserService.updateByPrimaryKeySelective(sysUser);
    }

    @GetMapping("/select/{userId}")
    public SysUser selectByPrimaryKey(@PathVariable("userId") Long userId) {
        return sysUserService.selectByPrimaryKey(userId);
    }

    @GetMapping("/selectAll")
    public List<SysUser> selectAll() {
        return sysUserService.selectAll();
    }

    // Add additional controller methods for more complex operations if needed
}

SysUserService

package com.example.readwritesplitting.demo;

/**
 * @ClassName SysUserService
 * @Description:
 * @Author: zpeng
 * @CreateDate: 2024/4/17 12:00
 */


import java.util.List;

public interface SysUserService {
    int insert(SysUser sysUser);

    int deleteByPrimaryKey(Long userId);

    int updateByPrimaryKeySelective(SysUser sysUser);

    SysUser selectByPrimaryKey(Long userId);

    List<SysUser> selectAll();
    // Additional service methods if needed
}

SysUserServiceImpl

package com.example.readwritesplitting.demo;

/**
 * @ClassName SysUserServiceImpl
 * @Description:
 * @Author: zpeng
 * @CreateDate: 2024/4/17 12:01
 */

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;


@Service
public class SysUserServiceImpl implements SysUserService {
    private final SysUserMapper sysUserMapper;

    @Autowired
    public SysUserServiceImpl(SysUserMapper sysUserMapper) {
        this.sysUserMapper = sysUserMapper;
    }

    @Override
    public int insert(SysUser sysUser) {
        return sysUserMapper.insert(sysUser);
    }

    @Override
    public int deleteByPrimaryKey(Long userId) {
        return sysUserMapper.deleteByPrimaryKey(userId);
    }

    @Override
    public int updateByPrimaryKeySelective(SysUser sysUser) {
        return sysUserMapper.updateByPrimaryKeySelective(sysUser);
    }

    @Override
    public SysUser selectByPrimaryKey(Long userId) {
        return sysUserMapper.selectByPrimaryKey(userId);
    }

    @Override
    public List<SysUser> selectAll() {
        return sysUserMapper.selectAll();
    }

    // Implement additional service methods if needed
}

SysUserMapper.xml

<?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">
<mapper namespace="com.example.readwritesplitting.demo.SysUserMapper">

    <insert id="insert" parameterType="com.example.readwritesplitting.demo.SysUser">
        INSERT INTO sys_user
        (dept_id, user_name, nick_name, user_type, email, phonenumber, sex, avatar, password,
         status, del_flag, login_ip, login_date, create_by, create_time, update_by, update_time, remark)
        VALUES (#{deptId}, #{userName}, #{nickName}, #{userType}, #{email}, #{phonenumber},
                #{sex}, #{avatar}, #{password}, #{status}, #{delFlag}, #{loginIp},
                #{loginDate}, #{createBy}, #{createTime}, #{updateBy}, #{updateTime}, #{remark})
    </insert>

    <delete id="deleteByPrimaryKey" parameterType="long">
        DELETE
        FROM sys_user
        WHERE user_id = #{userId}
    </delete>

    <update id="updateByPrimaryKeySelective" parameterType="com.example.readwritesplitting.demo.SysUser">
        UPDATE sys_user
        <set>
            <if test="deptId != null">dept_id = #{deptId},</if>
            <if test="userName != null">user_name = #{userName},</if>
            <!-- Add similar conditions for other fields -->
            <if test="updateTime != null">update_time = #{updateTime}</if>
        </set>
        WHERE user_id = #{userId}
    </update>

    <select id="selectByPrimaryKey" parameterType="long" resultType="com.example.readwritesplitting.demo.SysUser">
        SELECT *
        FROM sys_user
        WHERE user_id = #{userId}
    </select>

    <select id="selectAll" resultType="com.example.readwritesplitting.demo.SysUser">
        SELECT *
        FROM sys_user
    </select>

    <!-- Additional query mappings if needed -->
</mapper>

测试

启动

2024-04-17 15:18:47.109  INFO 4924 --- [           main] o.a.s.core.log.ConfigurationLogger       : MasterSlaveRuleConfiguration:
loadBalanceAlgorithmType: round_robin
masterDataSourceName: master
name: ms
slaveDataSourceNames:
- slave0
- slave1

2024-04-17 15:18:47.111  INFO 4924 --- [           main] o.a.s.core.log.ConfigurationLogger       : Properties:
sql.show: 'true'

http://localhost:8080/api/sys/users/select/1

使用的轮询测略

http://localhost:8080/api/sys/users/add

写走master

{
    "userId": "9999",
    "userName": "tempor",
    "nickName": "nick_name"
}

数据一致性

MySQL数据库的主从复制是一种常见的数据备份和高可用性解决方案。通过配置主从复制,可以实现将数据从一个MySQL服务器(主服务器)同步到另一个(从服务器),从而提高数据库的可用性和性能。