springboot 实现数据库的读写分离

springboot + mybatis +druid 主从数据库,所有select语句都走从数据库

在 Spring Boot 应用中,MyRoutingDataSource 可以与 DataSource 配置结合使用,通过 @Configuration 类来配置数据源和事务管理器,并使用 @Primary 注解来指定主数据源

添加依赖

复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.20</version> <!-- 根据需要选择合适的版本 -->
</dependency>

配置文件

复制代码
spring:
  datasource:
    master:
      jdbc-url: jdbc:mysql://localhost:3306/master_db?useSSL=false&serverTimezone=UTC
      username: master_user
      password: master_password
      driver-class-name: com.mysql.cj.jdbc.Driver
    slave1:
      jdbc-url: jdbc:mysql://localhost:3306/slave_db1?useSSL=false&serverTimezone=UTC
      username: slave_user
      password: slave_password
      driver-class-name: com.mysql.cj.jdbc.Driver
    slave2:
      jdbc-url: jdbc:mysql://localhost:3306/slave_db2?useSSL=false&serverTimezone=UTC
      username: slave_user
      password: slave_password
      driver-class-name: com.mysql.cj.jdbc.Driver

  # MyBatis配置
  mybatis:
    mapper-locations: classpath:/mapper/*.xml
    type-aliases-package: com.example.demo.model
    configuration:
      map-underscore-to-camel-case: true

# MyBatis配置,可以指定config-location来加载mybatis-config.xml配置文件
# mybatis.config-location: classpath:mybatis-config.xml

或者

复制代码
# 主数据源配置
spring.datasource.master.jdbc-url=jdbc:mysql://localhost:3306/master_db?useSSL=false&serverTimezone=UTC
spring.datasource.master.username=master_user
spring.datasource.master.password=master_password
spring.datasource.master.driver-class-name=com.mysql.cj.jdbc.Driver

# 从数据源配置
spring.datasource.slave1.jdbc-url=jdbc:mysql://localhost:3306/slave_db1?useSSL=false&serverTimezone=UTC
spring.datasource.slave1.username=slave_user
spring.datasource.slave1.password=slave_password
spring.datasource.slave1.driver-class-name=com.mysql.cj.jdbc.Driver

spring.datasource.slave2.jdbc-url=jdbc:mysql://localhost:3306/slave_db2?useSSL=false&serverTimezone=UTC
spring.datasource.slave2.username=slave_user
spring.datasource.slave2.password=slave_password
spring.datasource.slave2.driver-class-name=com.mysql.cj.jdbc.Driver

# MyBatis配置
mybatis.mapper-locations=classpath:/mapper/*.xml
mybatis.type-aliases-package=com.example.demo.model
mybatis.configuration.map-underscore-to-camel-case=true

# MyBatis配置,可以指定config-location来加载mybatis-config.xml配置文件
# mybatis.config-location=classpath:mybatis-config.xml

druid配置

复制代码
druid:
  initial-size: 5
  min-idle: 5
  max-active: 20
  max-wait: 60000

配置类

复制代码
@Configuration
public class DataSourceConfig {

    @Bean(name = "masterDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DataSource masterDataSource() {
        return DataSourceBuilder.create().type(DruidDataSource.class).build();
    }

    @Bean(name = "slave1DataSource")
    @ConfigurationProperties(prefix = "spring.datasource.slave1")
    public DataSource slaveDataSource() {
        return DataSourceBuilder.create().type(DruidDataSource.class).build();
    }
    @Bean(name = "slave2DataSource")
    @ConfigurationProperties(prefix = "spring.datasource.slave2")
    public DataSource slaveDataSource() {
        return DataSourceBuilder.create().type(DruidDataSource.class).build();
    }
}

定义数据源上下文持有者 (DbContextHolder):

这是一个用于存储和检索当前请求所选择的数据源类型的类,通常使用 ThreadLocal 来保证线程安全。

复制代码
public class DbContextHolder {
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

    public static void setDbType(String dbType) {
        contextHolder.set(dbType);
    }

    public static String getDbType() {
        return contextHolder.get();
    }

    public static void clearDbType() {
        contextHolder.remove();
    }
}

自定义一个数据源路由类MyRoutingDataSource 类,并且继承 AbstractRoutingDataSource,并且重写了 determineCurrentLookupKey() 方法,用于确定当前请求应该使用哪个数据源。这个方法的实现通常基于一些业务逻辑或者执行上下文来决定数据源的选择。

实现 determineCurrentLookupKey() 方法 : 在这个方法中,你可以通过调用 DbContextHolder.getDbType() 来获取当前请求的数据源类型。这个类型可以是一个简单的字符串、枚举值或其他可以唯一标识数据源的类型。

复制代码
public class MyRoutingDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        // 根据业务逻辑获取数据源类型
        String dbType = DbContextHolder.getDbType();
        if (dbType == null) {
            // 如果没有设置,可以使用默认的数据源类型
            dbType = "defaultDataSource";
        }
        if(dbType.equals("select")){
            dbType = "slaveDataSource"
        }
        return  dbType;
    }
}

配置路由

复制代码
@Configuration
public class DataSourceConfig {

    @Bean(name = "masterDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DataSource masterDataSource() {
        return DataSourceBuilder.create().type(DruidDataSource.class).build();
    }

    @Bean(name = "slave1DataSource")
    @ConfigurationProperties(prefix = "spring.datasource.slave1")
    public DataSource slaveDataSource() {
        return DataSourceBuilder.create().type(DruidDataSource.class).build();
    }
    @Bean(name = "slave2DataSource")
    @ConfigurationProperties(prefix = "spring.datasource.slave2")
    public DataSource slaveDataSource() {
        return DataSourceBuilder.create().type(DruidDataSource.class).build();
    }

    @Bean
    public MyRoutingDataSource dataSource() {
        MyRoutingDataSource dataSource = new MyRoutingDataSource();
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put("masterDataSource", masterDataSource());
        targetDataSources.put("slave1DataSource", slaveDataSource());
        targetDataSources.put("slave2DataSource", slaveDataSource());
        dataSource.setTargetDataSources(targetDataSources);
        dataSource.setDefaultTargetDataSource(masterDataSource()); // 设置默认数据源
        return dataSource;
    }

}

使用 AOP 拦截 SQL 语句

复制代码
@Aspect
@Component
public class DataSourceAspect {

    @Before("execution(* org.springframework.jdbc.core.JdbcTemplate.query(..))")
    public void setReadDataSource() {
        // 所有查询操作前设置使用从数据库
        DbContextHolder.setDbType("select");
    }

    @After("execution(* org.springframework.jdbc.core.JdbcTemplate.query(..))")
    public void clearReadDataSource() {
        // 查询操作后清除设置
        DbContextHolder.clearDbType();
    }

    // 可以添加更多的切点来处理其他类型的数据操作
}

配置事务管理器

复制代码
@Configuration
public class DataSourceConfig {

    @Bean(name = "masterDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DataSource masterDataSource() {
        return DataSourceBuilder.create().type(DruidDataSource.class).build();
    }

    @Bean(name = "slave1DataSource")
    @ConfigurationProperties(prefix = "spring.datasource.slave1")
    public DataSource slaveDataSource() {
        return DataSourceBuilder.create().type(DruidDataSource.class).build();
    }
    @Bean(name = "slave2DataSource")
    @ConfigurationProperties(prefix = "spring.datasource.slave2")
    public DataSource slaveDataSource() {
        return DataSourceBuilder.create().type(DruidDataSource.class).build();
    }

    @Bean(name = "masterTransactionManager")
public PlatformTransactionManager masterTransactionManager(@Qualifier("masterDataSource") DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource);
}

@Bean(name = "slaveTransactionManager1")
public PlatformTransactionManager slaveTransactionManager1(@Qualifier("slave2DataSource") DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource);
}
@Bean(name = "slaveTransactionManager2")
public PlatformTransactionManager slaveTransactionManager1(@Qualifier("slave2DataSource") DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource);
}

    @Bean
    public MyRoutingDataSource dataSource() {
        MyRoutingDataSource dataSource = new MyRoutingDataSource();
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put("masterDataSource", masterDataSource());
        targetDataSources.put("slave1DataSource", slaveDataSource());
        targetDataSources.put("slave2DataSource", slaveDataSource());
        dataSource.setTargetDataSources(targetDataSources);
        dataSource.setDefaultTargetDataSource(masterDataSource()); // 设置默认数据源
        return dataSource;
    }

}

以上就配置好了

另外如果你对性能和可扩展性要求比较高,还可以为每个数据源创建不同的MyBatis会话工厂。可以为每个数据源配置SqlSessionFactory Bean,并注入对应的数据源。

复制代码
@Bean
public SqlSessionFactory masterSqlSessionFactory(@Qualifier("masterDataSource") DataSource masterDataSource) throws Exception {
    SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
    sessionFactory.setDataSource(masterDataSource);
    sessionFactory.setMapperLocations(mapperLocations());
    // 其他MyBatis配置...
    return sessionFactory.getObject();
}

@Bean
public SqlSessionFactory slaveSqlSessionFactory(@Qualifier("slaveDataSource") DataSource slaveDataSource) throws Exception {
    SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
    sessionFactory.setDataSource(slaveDataSource);
    sessionFactory.setMapperLocations(slaveMapperLocations());
    // 其他MyBatis配置...
    return sessionFactory.getObject();
}

配置MyBatis模板 :为每个会话工厂配置SqlSessionTemplate

复制代码
@Bean
public SqlSessionTemplate masterSqlSessionTemplate(@Qualifier("masterSqlSessionFactory") SqlSessionFactory masterSqlSessionFactory) {
    return new SqlSessionTemplate(masterSqlSessionFactory);
}

@Bean
public SqlSessionTemplate slaveSqlSessionTemplate(@Qualifier("slaveSqlSessionFactory") SqlSessionFactory slaveSqlSessionFactory) {
    return new SqlSessionTemplate(slaveSqlSessionFactory);
}
相关推荐
数据库小组7 小时前
2026 年,MySQL 到 SelectDB 同步为何更关注实时、可观测与可校验?
数据库·mysql·数据库管理工具·数据同步·ninedata·selectdb·迁移工具
华科易迅7 小时前
MybatisPlus增删改查操作
android·java·数据库
WZTTMoon7 小时前
Spring Boot 中Servlet、Filter、Listener 四种注册方式全解析
spring boot·后端·servlet
Kethy__7 小时前
计算机中级-数据库系统工程师-计算机体系结构与存储系统
大数据·数据库·数据库系统工程师·计算机中级
SHoM SSER7 小时前
MySQL 数据库连接池爆满问题排查与解决
android·数据库·mysql
standovon8 小时前
Spring Boot整合Redisson的两种方式
java·spring boot·后端
熬夜的咕噜猫8 小时前
MySQL备份与恢复
数据库·oracle
jnrjian8 小时前
recover database using backup controlfile until cancel 假recover,真一致
数据库·oracle
zs宝来了9 小时前
Spring Boot 自动配置原理:@EnableAutoConfiguration 的魔法
spring boot·自动配置·源码解析·enableautoconfiguration
lifewange9 小时前
java连接Mysql数据库
java·数据库·mysql