SpringBoot - MyBatis-Plus使用详解20(多数据源)
二十、多数据源
1,基本介绍
(1)dynamic-datasource-spring-boot-starter 是一个基于 springboot 的快速集成多数据源的启动器。其支持 Jdk 1.7+,SpringBoot 1.4.x 1.5.x 2.0.x。
注意:该插件可以不依赖 MyBatis-Plus,可以独立使用。
(2)它主要用于读写分离,一主多从的环境。(当然也可以纯粹当成多库使用)
一主多从的环境下数据库分工:
- 主数据库建议只执行 INSERT、UPDATE、DELETE 操作。
- 从数据库建议只执行 SELECT 操作。
2,功能特性
- 数据源分组,适用于多种场景:纯粹多库、读写分离、一主多从、混合模式。
- 内置敏感参数加密和启动初始化表结构 schema 数据库 database。
- 提供对 Druid,Mybatis-Plus,P6sy,Jndi 的快速集成。
- 简化 Druid 和 HikariCp 配置,提供全局参数配置。
- 提供自定义数据源来源接口(默认使用 yml 或 properties 配置)。
- 提供项目启动后增减数据源方案。
- 提供 Mybatis 环境下的 纯读写分离 方案。
- 使用 spel 动态参数解析数据源,如从 session,header 或参数中获取数据源。(多租户架构神器)
- 提供多层数据源嵌套切换。(ServiceA >>> ServiceB >>> ServiceC,每个 Service 都是不同的数据源)
- 提供“不使用注解”而使用“正则”或 spel 来切换数据源方案(实验性功能)。
- 基于 seata 的分布式事务支持。
3,安装配置
(1)首先编辑项目的 pom.xml 文件,引入 dynamic-datasource-spring-boot-starter 依赖:
<!-- 多数据源 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>3.1.1</version> </dependency>
(2)接着配置数据源,比如下面我们配置了一个主库、两个从库。
提示:
- 配置文件中所有以下划线 _ 分割的数据源“首部”即为组的名称,相同组名称的数据源会放在一个组下。
- 默认的数据源名称为 master ,我们可以通过 spring.datasource.dynamic.primary 修改。
spring:
datasource:
dynamic:
primary: master #设置默认的数据源或者数据源组,默认值即为master
strict: false #设置严格模式,默认false不启动. 启动后在未匹配到指定数据源时候回抛出异常,不启动会使用默认数据源.
datasource:
master:
url: jdbc:mysql://xx.xx.xx.xx:3306/hangge?serverTimezone=Asia/Shanghai
username: root
password: hangge1234
type: com.alibaba.druid.pool.DruidDataSource
slave_1:
url: jdbc:mysql://xx.xx.xx.xx:3307/hangge?serverTimezone=Asia/Shanghai
username: root
password: hangge1234
type: com.alibaba.druid.pool.DruidDataSource
slave_2:
url: jdbc:mysql://xx.xx.xx.xx:3308/hangge?serverTimezone=Asia/Shanghai
username: root
password: hangge1234
type: com.alibaba.druid.pool.DruidDataSource
- 或者我们也可以配置多主多从:
spring:
datasource:
dynamic:
datasource:
master_1:
master_2:
slave_1:
slave_2:
slave_3:
- 或者纯粹多库(记得设置 primary):
spring:
datasource:
dynamic:
datasource:
mysql:
oracle:
sqlserver:
postgresql:
h2:
- 或者混合配置:
spring:
datasource:
dynamic:
datasource:
master:
slave_1:
slave_2:
oracle_1:
oracle_2:
(3)最后代码中使用 @DS 切换数据源。
- @DS 可以注解在方法上和类上。如果同时存在,方法注解优先于类上注解。强烈建议注解在 service 实现或 mapper 接口方法上。
- @DS("xxx") 指定使用 xxx 这个数据源,xxx 可以为组名也可以为具体某个库的名称。如果是组名则切换时采用负载均衡算法切换。如果指定的组名或者库不存在,则自动使用默认数据源(主库)
- 如果没有 @DS,则使用默认数据源(主库)
- 如果设置了 @DS 但没有指定某个组或者库,则根据 DynamicDataSourceStrategy 策略,选择一个从库。默认负载均衡策略。
@Service @DS("slave") public class UserServiceImpl implements UserService { @Autowired private JdbcTemplate jdbcTemplate; public List<Map<String, Object>> selectAll() { return jdbcTemplate.queryForList("select * from user"); } @Override @DS("slave_1") public List<Map<String, Object>> selectByCondition() { return jdbcTemplate.queryForList("select * from user where age >10"); } }
- 在 mybatis 环境下也可注解在 mapper 接口层。比如下面我们在主数据库上执行 INSERT、UPDATE、DELETE 操作,而从数据库执行 SELECT 操作。
@DS("master") public interface UserMapper { @Insert("INSERT INTO user (name,age) values (#{name},#{age})") boolean addUser(@Param("name") String name, @Param("age") Integer age); @Update("UPDATE user set name=#{name}, age=#{age} where id =#{id}") boolean updateUser(@Param("id") Integer id, @Param("name") String name, @Param("age") Integer age); @Delete("DELETE from user where id =#{id}") boolean deleteUser(@Param("id") Integer id); @Select("SELECT * FROM user") @DS List<User> selectAll(); }
附:样例演示
1,基本用法
(1)假设我们有 hangge 与 hangge2 两个数据库,两个库中都有 user_info 这张表(不过两个库里数据不一样):
(2)在配置文件中一个设置为主库,一个设置为从库:
spring:
datasource:
dynamic:
primary: master #设置默认的数据源或者数据源组,默认值即为master
strict: false #设置严格模式,默认false不启动. 启动后在未匹配到指定数据源时候回抛出异常,不启动会使用默认数据源.
datasource:
master:
url: jdbc:mysql://localhost:3306/hangge?serverTimezone=Asia/Shanghai
username: root
password: hangge1234
type: com.alibaba.druid.pool.DruidDataSource
slave:
url: jdbc:mysql://localhost:3306/hangge2?serverTimezone=Asia/Shanghai
username: root
password: hangge1234
type: com.alibaba.druid.pool.DruidDataSource
(3)默认情况下,如果不做额外配置的话,都是操作主库:
@RestController public class HelloController { @Autowired UserInfoService userInfoService; @GetMapping("/test") public List<UserInfo> test() { return userInfoService.list(); } }
(4)使用 @DS 切换数据源,这里我们将 UserInfoService 数据源切换成从库。再次进行查询,可以发现数据源确实发生变化。
@DS("slave") public interface UserInfoService extends IService<UserInfo> { }
2,负载均衡
(1)如果数据源指定的是一个分组,且该分组下有多个数据库,那么会自动进行负载均衡。比如我们在上面样例上稍作修改,配置了两个从库:spring:
datasource:
dynamic:
primary: master #设置默认的数据源或者数据源组,默认值即为master
strict: false #设置严格模式,默认false不启动. 启动后在未匹配到指定数据源时候回抛出异常,不启动会使用默认数据源.
datasource:
master:
url: jdbc:mysql://localhost:3306/hangge?serverTimezone=Asia/Shanghai
username: root
password: hangge1234
type: com.alibaba.druid.pool.DruidDataSource
slave_1:
url: jdbc:mysql://localhost:3306/hangge2?serverTimezone=Asia/Shanghai
username: root
password: hangge1234
type: com.alibaba.druid.pool.DruidDataSource
slave_2:
url: jdbc:mysql://localhost:3306/hangge?serverTimezone=Asia/Shanghai
username: root
password: hangge1234
type: com.alibaba.druid.pool.DruidDataSource
(2)UserInfoService 同样通过 @DS 注解指定使用 slave 这个分组:
@DS("slave") public interface UserInfoService extends IService<UserInfo> { }
(3)多次刷新浏览器,可以发现数据轮流从两个库中获取,说明负载均衡生效了。