SpringBoot 多数据源配置(读写分离基础)

随着项目变大,单数据扛不住压力很常见的事,这时候一般会先做读写分离 :主库、从库读,减轻单库压力。而实现读写分离的第一,就是先学会在 SringBoot 里配置多数据源


一、适用场景

  • • 主库写入、从库读取(读写分离基础版)

  • • 一个项目连接多个不同业务库

  • • 微服务内部多库访问

  • • 简单分库、不分表的场景

二、整体思路

    1. 关闭 SpringBoot 默认数据源自动配置
    1. 手动构建两个(或多个)DataSource
    1. 为每个数据源配置独立的 SqlSessionFactorySqlSessionTemplate
    1. 按包或按注解切换数据源
    1. 事务注意:多数据源下跨库事务需要额外处理

三、引入依赖

和单数据源 MyBatis 一致:

go 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.3.2</version>
</dependency>
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

四、application.yml 配置双数据源

这里配置一个主库(write)、一个从库(read):

go 复制代码
spring:
  # 关闭自动配置数据源
  autoconfigure:
    exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration

# 主库(写)
datasource:
  master:
    url: jdbc:mysql://localhost:3306/testdb_master?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

  # 从库(读)
  slave:
    url: jdbc:mysql://localhost:3306/testdb_slave?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

# MyBatis 通用配置
mybatis:
  configuration:
    map-underscore-to-camel-case: true

五、手动配置主数据源(Master)

新建配置类:DataSourceMasterConfig

go 复制代码
package com.demo.config;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;

@Configuration
// 主库 Mapper 接口包路径
@MapperScan(basePackages = "com.demo.mapper.master",
            sqlSessionTemplateRef = "masterSqlSessionTemplate")
public class DataSourceMasterConfig {

    @Bean
    @ConfigurationProperties(prefix = "datasource.master")
    @Primary   // 标记为主数据源
    public DataSource masterDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    @Primary
    public SqlSessionFactory masterSqlSessionFactory(
            @Qualifier("masterDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        // XML 路径
        bean.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath:mapper/master/*.xml"));
        return bean.getObject();
    }

    @Bean
    @Primary
    public SqlSessionTemplate masterSqlSessionTemplate(
            @Qualifier("masterSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

六、手动配置从数据源(Slave)

新建配置类:DataSourceSlaveConfig

go 复制代码
package com.demo.config;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;

@Configuration
// 从库 Mapper 接口包路径
@MapperScan(basePackages = "com.demo.mapper.slave",
            sqlSessionTemplateRef = "slaveSqlSessionTemplate")
public class DataSourceSlaveConfig {

    @Bean
    @ConfigurationProperties(prefix = "datasource.slave")
    public DataSource slaveDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    public SqlSessionFactory slaveSqlSessionFactory(
            @Qualifier("slaveDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath:mapper/slave/*.xml"));
        return bean.getObject();
    }

    @Bean
    public SqlSessionTemplate slaveSqlSessionTemplate(
            @Qualifier("slaveSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionTemplate);
    }
}

七、项目结构划分(按包区分数据源)

go 复制代码
com.demo
├── mapper
│   ├── master        # 主库:写操作
│   │   └── UserMasterMapper.java
│   └── slave         # 从库:读操作
│       └── UserSlaveMapper.java
├── service
│   ├── MasterService
│   └── SlaveService
└── config
    ├── DataSourceMasterConfig.java
    └── DataSourceSlaveConfig.java

resources/mapper 下也要对应:

go 复制代码
resources/mapper/master/*.xml
resources/mapper/slave/*.xml

这样不同包下的 Mapper 会自动走不同数据源,实现写走主库、读走从库


八、Service 层使用示例

go 复制代码
@Service
public class UserService {

    @Autowired
    private UserMasterMapper userMasterMapper;  // 主库:写

    @Autowired
    private UserSlaveMapper userSlaveMapper;    // 从库:读

    // 写操作:主库
    @Transactional
    public int addUser(User user) {
        return userMasterMapper.insert(user);
    }

    // 读操作:从库
    public User getUserById(Long id) {
        return userSlaveMapper.selectById(id);
    }
}

九、多数据源事务说明

  • • 单个数据源内事务正常使用 @Transactional

  • 跨多个数据源的事务不保证原子性

  • • 真正生产级读写分离一般用:

    • • Sharding-JDBC

    • • MyCat

    • • dynamic-datasource-spring-boot-starter

本文这种手动配置方式,适合学习原理、简单多库场景,真正高可用建议用成熟组件。


十、注意事项

    1. 没关闭 DataSourceAutoConfiguration → 启动报错
    1. @Primary 忘记加 → 找不到首选数据源
    1. Mapper 包路径、XML 路径写错 → 绑定失败
    1. 多个库表结构不一致 → 查询报错
    1. 误以为自动支持分布式事务 → 踩大坑

十一、总结

SpringBoot 多数据源核心就三步:

    1. 关闭自动配置
    1. 手动注册多个 DataSource
    1. 按包/按注解区分 SqlSessionFactory

掌握这一套,你就理解了读写分离的底层原理,再去用 Sharding-JDBC、dynamic-datasource 这类框架会非常轻松。

相关推荐
掘金者阿豪1 小时前
Django接金仓数据库:我踩过的坑和填坑指南
后端
椰羊~王小美1 小时前
@RequestMapping注解的各个属性作用
java
_风满楼1 小时前
HTTP 请求的五种传参方式
前端·javascript·后端
码事漫谈1 小时前
为什么 token 计费规则里,输出比输入贵那么多
后端
Go_error2 小时前
Go database/sql 基于临时 channel 传递连接
后端·go
Yeh2020582 小时前
request与response笔记
java·前端·笔记
Go_error2 小时前
Go 循环栅栏
后端·go
程序员老邢2 小时前
【产品底稿 07】商助慧 Admin 运维模块落地:从 “能跑” 到 “能运维”,3 个页面搞定日常排障
java·运维·经验分享·spring boot·后端
彩票管理中心秘书长2 小时前
npm 依赖管理机制完全解析(超详细版)
后端
彩票管理中心秘书长2 小时前
npm 脚本与自动化完全指南(超详细版)
后端