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 这类框架会非常轻松。

相关推荐
葫芦和十三4 小时前
图解 MongoDB 19|Oplog:复制的真正载体,不是文档是操作
后端·mongodb·agent
葫芦和十三4 小时前
图解 MongoDB 20|复制延迟与 catch up:Secondary 为什么跟不上
后端·mongodb·agent
IT_陈寒9 小时前
SpringBoot自动配置的坑,我的API突然就404了
前端·人工智能·后端
ServBay10 小时前
为什么说 MCP 是 2026 年开发者必须掌握的黄金协议?
后端·mcp
程序员夏洛10 小时前
Spring Boot 多模块项目中 IDEA 提示 Cannot resolve symbol 的一次排查记录
后端
子兮曰10 小时前
OpenMontage 深度解剖:你的 AI 编程助手,其实是个视频工作室
前端·后端·ai编程
子兮曰10 小时前
前端工具链的「Rust 化」:一场没有赢家的军备竞赛?
前端·后端·rust
爱勇宝11 小时前
从 Ctrl+CV 到 Enter:程序员正在失去什么
前端·后端·程序员
码事漫谈11 小时前
EdgeOne Makers + WorkBuddy:零基础也能快速搭建可上线的 AI 智能体(附图文教程)
后端