在本文中使用Spring Boot 2.4.1+MyBatis-plus+Druid+Sharding-JDBC+MySQL进行读写分离的案件讲解。
1、数据库准备
1、192.168.8.162 test1主
2、192.168.8.134 test1从
3、192.168.8.176 test1从
4、192.168.8.162 test2主
5、192.168.8.134 test2从
6、192.168.8.176 test2从
2、准备分库分表
USE `test1`; DROP TABLE IF EXISTS `t_user_0`; CREATE TABLE `t_user_0` ( `id` int(10) NOT NULL, `name` varchar(50) NOT NULL, `sex` varchar(10) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; DROP TABLE IF EXISTS `t_user_1`; CREATE TABLE `t_user_1` ( `id` int(10) NOT NULL, `name` varchar(50) NOT NULL, `sex` varchar(10) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; DROP TABLE IF EXISTS `t_user_2`; CREATE TABLE `t_user_2` ( `id` int(10) NOT NULL, `name` varchar(50) NOT NULL, `sex` varchar(10) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; DROP TABLE IF EXISTS `t_user_3`; CREATE TABLE `t_user_3` ( `id` int(10) NOT NULL, `name` varchar(50) NOT NULL, `sex` varchar(10) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; USE `test2`; DROP TABLE IF EXISTS `t_user_0`; CREATE TABLE `t_user_0` ( `id` int(10) NOT NULL, `name` varchar(50) NOT NULL, `sex` varchar(10) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; DROP TABLE IF EXISTS `t_user_1`; CREATE TABLE `t_user_1` ( `id` int(10) NOT NULL, `name` varchar(50) NOT NULL, `sex` varchar(10) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; DROP TABLE IF EXISTS `t_user_2`; CREATE TABLE `t_user_2` ( `id` int(10) NOT NULL, `name` varchar(50) NOT NULL, `sex` varchar(10) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; DROP TABLE IF EXISTS `t_user_3`; CREATE TABLE `t_user_3` ( `id` int(10) NOT NULL, `name` varchar(50) NOT NULL, `sex` varchar(10) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
3、上代码
1、pom.xml配置引入maven依赖
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<druid.version>1.1.9</druid.version>
<mybatis-plus.version>3.1.1</mybatis-plus.version>
<mybatis-plus-stater.version>3.1.2</mybatis-plus-stater.version>
<lombok.version>1.16.18</lombok.version>
<mybatisplus-spring-boot-starter.version>1.0.5</mybatisplus-spring-boot-starter.version>
</properties>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- Mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatisplus-spring-boot-starter</artifactId>
<version>${mybatisplus-spring-boot-starter.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus-stater.version}</version>
</dependency>
<!--shardingsphere数据分片、脱敏工具-->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.1.0</version>
</dependency>
2、在application.yml中配置使用mybatis-plus及引用数据源及分库分表信息
spring: # 配置说明地址 https://test1.apache.org/document/legacy/4.x/document/cn/manual/sharding-jdbc/configuration/config-spring-boot/#%E6%95%B0%E6%8D%AE%E5%88%86%E7%89%87 shardingsphere: # 数据库 datasource: # 主库1 ,master数据库 master0: ### 数据源类别 type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.8.162:3306/test1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8 username: root password: root # 主库1从库1 ,slave数据库 master0slave0: ### 数据源类别 type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.8.134:3306/test1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8 username: root password: root # 主库1从库2 ,slave数据库 master0slave1: ### 数据源类别 type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.8.176:3306/test1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8 username: root password: root # 主库2 ,master数据库 master1: ### 数据源类别 type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.8.162:3306/test2?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8 username: root password: root # 主库2从库1 ,slave数据库 master1slave0: ### 数据源类别 type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.8.134:3306/test2?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8 username: root password: root # 主库2从库2 ,slave数据库 master1slave1: ### 数据源类别 type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.8.176:3306/test2?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8 username: root password: root # 数据库的别名 names: master0,master0slave0,master0slave1,master1,master1slave0,master1slave1 sharding: # 设置绑定表,用逗号分割 binding-tables: t_user master-slave-rules: ds0: name: ds0datasource # 查询时的负载均衡算法,目前有2种算法,round_robin(轮询)和random(随机), # 算法接口是io.shardingjdbc.core.api.algorithm.masterslave.MasterSlaveLoadBalanceAlgorithm。 # 实现类有RandomMasterSlaveLoadBalanceAlgorithm 和 RoundRobinMasterSlaveLoadBalanceAlgorithm。 load-balance-algorithm-type: round_robin # 主数据源名称 master-data-source-name: master0 # 从数据源名称,多个用逗号隔开 slave-data-source-names: master0slave0, master0slave1 ds1: name: ds1datasource # 查询时的负载均衡算法,目前有2种算法,round_robin(轮询)和random(随机), # 算法接口是io.shardingjdbc.core.api.algorithm.masterslave.MasterSlaveLoadBalanceAlgorithm。 # 实现类有RandomMasterSlaveLoadBalanceAlgorithm 和 RoundRobinMasterSlaveLoadBalanceAlgorithm。 load-balance-algorithm-type: round_robin # 主数据源名称 master-data-source-name: master1 # 从数据源名称,多个用逗号隔开 slave-data-source-names: master1slave0,master1slave1 tables: ### t_user分库分表配置 t_user: actual-data-nodes: ds$->{0..1}.t_user_$->{0..3} database-strategy: standard: precise-algorithm-class-name: com.demo.shardingjdbc.MyDBPreciseShardingAlgorithm sharding-column: id table-strategy: standard: precise-algorithm-class-name: com.demo.shardingjdbc.MyTablePreciseShardingAlgorithm sharding-column: id #### mybatis-plus ### mybatis-plus: # 如果是放在src/main/java目录下 classpath:/com/yourpackage/*/mapper/*Mapper.xml # 如果是放在resource目录 classpath:/mapper/*Mapper.xml mapper-locations: classpath:mapper/*.xml #实体扫描,多个package用逗号或者分号分隔 type-aliases-package: com.demo.shardingjdbc.entity configuration: map-underscore-to-camel-case: true cache-enabled: false #主键类型 0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID"; global-config: db-config: id-type: auto #字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断" field-strategy: not-empty #驼峰下划线转换 column-underline: true #逻辑删除配置 logic-delete-value: 0 logic-not-delete-value: 1 db-type: mysql #刷新mapper 调试神器 refresh: false
3、配置分库分表分片规则(结合application.yml)
分库规则(结合pplication.yml中database-strategy)
package com.demo.shardingjdbc; import java.util.Collection; import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm; import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue; /** * 自定义实现 精准分片算法(PreciseShardingAlgorithm)接口 * 数据表table的精准分片 * @author hzy * */ public class MyDBPreciseShardingAlgorithm implements PreciseShardingAlgorithm<Integer> { @Override public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Integer> shardingValue) { for (String tableName : availableTargetNames) { if (tableName.endsWith(shardingValue.getValue() % 2 + "")) { return tableName; } } throw new IllegalArgumentException(); } }
分表规则(结合pplication.yml中table-strategy)
package com.demo.shardingjdbc; import java.util.Collection; import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm; import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue; /** * 自定义实现 精准分片算法(PreciseShardingAlgorithm)接口 * 数据表table的精准分片 * @author hzy * */ public class MyTablePreciseShardingAlgorithm implements PreciseShardingAlgorithm<Integer> { @Override public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Integer> shardingValue) { for (String tableName : availableTargetNames) { if (tableName.endsWith(shardingValue.getValue() % 4 + "")) { return tableName; } } throw new IllegalArgumentException(); } }
4、mybatis-plus操作数据库配置
User.java
package com.demo.shardingjdbc.entity;
import java.io.Serializable;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* @Description: (User)实体类
* @Version 1.0
*/
@Data
@TableName("t_user")
public class User implements Serializable {
private static final long serialVersionUID = 358157380505039579L;
/**
* 用户id
*/
@TableId(type = IdType.INPUT)
private Integer id;
/**
* 用户名称
*/
private String name;
/**
* 性别
*/
private String sex;
}
mapper
package com.demo.shardingjdbc.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.demo.shardingjdbc.entity.User;
/**
* (t_user)表数据库访问层
*
*/
public interface UserMapper extends BaseMapper<User> {
}
5、mybatis-plus配置MybatisPlusConfig
package com.demo.shardingjdbc.config;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* @Description:
*/
//Spring boot方式
@EnableTransactionManagement
@Configuration
//扫描的mapper文件路径
@MapperScan(value = "com.demo.shardingjdbc.mapper")
public class MybatisPlusConfig {
/**
* 分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
6、service层
package com.demo.shardingjdbc.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.demo.shardingjdbc.entity.User;
import com.demo.shardingjdbc.mapper.UserMapper;
import com.demo.shardingjdbc.service.UserService;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public List<User> selectList() {
QueryWrapper<User> wrapper = new QueryWrapper<User>();
wrapper.orderByAsc("id");
List<User> users = userMapper.selectList(wrapper);
return users;
}
@Override
public int insert(User user) {
return userMapper.insert(user);
}
}
7、controller控制层
package com.demo.shardingjdbc.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.demo.shardingjdbc.entity.User;
import com.demo.shardingjdbc.service.UserService;
import lombok.extern.slf4j.Slf4j;
/**
* @ClassName TestController
* @Description TestController
* @Version
*/
@RestController
@Slf4j
public class UserController {
@Autowired
private UserService userService;
/**
* 用户列表
*
* @return
*/
@RequestMapping("/userList")
public List<User> userList() {
log.info("********TestController userList()");
List<User> users = userService.selectList();
return users;
}
/**
* 保存用户
*
* @return
*/
@GetMapping("/add")
public Object add() {
int num = 0;
for (int i = 1; i <= 300; i++) {
User user = new User();
user.setId(i);
user.setName("hzy" + (i));
String sex = (i % 2 == 0) ? "男" : "女";
user.setSex(sex);
int resutl = userService.insert(user);
log.info("insert:" + user.toString() + " result:" + resutl);
num = num + resutl;
}
return num;
}
}
完成。在浏览器上执行localhost:8080/add,然后去数据库中查询,可以看到test1.t_user_0、test1.t_user_2、test2.t_user_1、test2.t_user_3分别插入了数据。
然后访问localhost:8080/userList,可以查询数据库中四个表中的所有数据。可见Sharding-JDBC在插入数据的时候,根据数据分库分表策略,将数据存储在不同库不同表中,查询时将数据库从多个表中查询并聚合。
