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

相关推荐
十贝15 小时前
Tailscale 自建 DERP 中继服务器故障排查与修复记录
后端
Nontee15 小时前
Java 后端面试题目全集
java·开发语言·面试
用户67570498850216 小时前
Redis有1亿个Key,如何优雅地找出特定前缀的那10万条?
后端
jran-16 小时前
Docker dockerfile镜像制作&compose服务编排&私有仓库
java·docker·容器
Chase_______16 小时前
【Java杂项】0.1 + 0.2 为什么不等于 0.3?IEEE 754 与 BigDecimal 精度避坑
java·开发语言·python
ch.ju16 小时前
Java Programming Chapter 4——Static part
java·开发语言
用户67570498850216 小时前
程序员常犯的坑:别再用 VARCHAR 存 IP 了!用对方式,性能何止提升10倍!
后端
YDS82916 小时前
DeepSeek RAG&MCP + Agent智能体项目 —— 环境搭建和项目初始化
java·springboot·agent·rag·deepseek
ChoSeitaku16 小时前
04.数组
java·开发语言·数据结构
老马952716 小时前
opencode8-桌面应用实战 3
前端·人工智能·后端