Spring Boot + MyBatis-Plus 实现数据库读写分离
在现代的微服务架构中,数据库读写分离是一种常见的架构设计,通过将读操作和写操作分开,不仅可以提高系统的吞吐量,还能优化数据库的负载能力。本文将介绍如何在 Spring Boot 项目中结合 MyBatis-Plus 实现数据库的读写分离。
一、项目环境准备
- Spring Boot: 确保你已经搭建好了 Spring Boot 项目。
- MyBatis-Plus: 在
pom.xml
中加入 MyBatis-Plus 的依赖。
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version>
</dependency>
- 数据库: 准备好一台支持读写分离的数据库实例,通常情况下可以是 MySQL。
二、配置数据源
在 application.yml
配置文件中配置主从数据源。我们将设置主数据库用于写操作,从数据库用于读操作。
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
hikari:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: password
jdbc-url: jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
maximum-pool-size: 10
# 从数据源配置
spring:
datasource:
read:
type: com.zaxxer.hikari.HikariDataSource
hikari:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: password
jdbc-url: jdbc:mysql://localhost:3306/mydb_slave?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
maximum-pool-size: 10
三、创建数据源路由
我们需要创建一个数据源路由,以决定何时使用主库(用于写操作)和从库(用于读操作)。
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContext.getCurrentDataSource();
}
}
public class DataSourceContext {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
public static void setCurrentDataSource(String dataSource) {
contextHolder.set(dataSource);
}
public static String getCurrentDataSource() {
return contextHolder.get();
}
public static void clear() {
contextHolder.remove();
}
}
四、配置 MyBatis-Plus
在 MyBatis-Plus 配置中,我们需要设置动态数据源。
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
public class MyBatisPlusConfig {
@Bean
public DynamicDataSource dataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("master", masterDataSource());
targetDataSources.put("read", readDataSource());
dynamicDataSource.setTargetDataSources(targetDataSources);
dynamicDataSource.setDefaultTargetDataSource(masterDataSource());
return dynamicDataSource;
}
@Bean
public DataSource masterDataSource() {
// 创建主数据源
}
@Bean
public DataSource readDataSource() {
// 创建从数据源
}
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
五、使用注解切换数据源
为了便于操作,我们可以创建一个自定义注解来标识读写操作。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ReadOnly {
}
接下来,我们可以实现一个 AOP 切面,根据方法的注解来切换数据源。
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class DataSourceSwitchAspect {
@Before("@annotation(ReadOnly)")
public void switchToRead() {
DataSourceContext.setCurrentDataSource("read");
}
@Before("!@annotation(ReadOnly)")
public void switchToWrite() {
DataSourceContext.setCurrentDataSource("master");
}
}
六、使用示例
接下来,我们可以在 Service 层中使用上述注解进行读写操作。
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
@Service
public class UserService extends ServiceImpl<UserMapper, User> {
@ReadOnly
public User getUserById(Long id) {
return this.getById(id);
}
public void createUser(User user) {
this.save(user);
}
}
七、总结
通过上述步骤,我们成功实现了在 Spring Boot 项目中使用 MyBatis-Plus 的数据库读写分离。我们定义了动态数据源与 AOP 切点,能够根据操作类型轻松地切换数据库,提高了系统的可扩展性与性能。在实际项目中,读写分离是一种非常有效的解决方案,通过合理的架构设计,可以显著提升数据库的并发访问能力。