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

相关推荐
派大星酷2 小时前
线程池-----Executors
java·开发语言
小碗羊肉2 小时前
【从零开始学Java | 第二十八篇】可变参数
java·开发语言
Java成神之路-2 小时前
Spring AOP 核心进阶:切入点表达式 + 通知类型 + 环绕通知避坑指南(Spring系列8)
java·后端·spring
清汤饺子2 小时前
Cursor + Claude Code 组合使用心得:我为什么不只用一个 AI 编程工具
前端·javascript·后端
weitingfu2 小时前
Excel VBA 入门到精通(二):变量、数据类型与运算符
java·大数据·开发语言·学习·microsoft·excel·vba
无责任此方_修行中2 小时前
Redis 的"三面"人生:开源世界的权力转移
redis·后端·程序员
某人辛木2 小时前
Maven一步到位
java·maven
一条咸鱼_SaltyFish2 小时前
DDD 架构重构实践:AI Skills 如何赋能DDD设计与重构
java·人工智能·ai·重构·架构·ddd·领域驱动设计
想唱rap2 小时前
线程之条件变量和生产消费模型
java·服务器·开发语言·数据库·mysql·ubuntu