引言
数据库读写分离是大型系统中常见的性能优化方案之一。通过将读操作和写操作分别分配到不同的数据库节点,可以有效地提高数据库的处理能力和稳定性。本文将介绍如何使用Spring Boot和MyBatis-Plus来实现数据库的读写分离。
1. 环境准备
在开始之前,需要准备以下环境:
- JDK 1.8或以上版本
- Maven构建工具
- MySQL数据库
2. 创建Spring Boot项目
首先,我们需要创建一个Spring Boot项目。可以使用Maven命令创建一个基本的Spring Boot项目骨架:
mvn archetype:generate -DgroupId=com.example -DartifactId=read-write-split -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
然后在pom.xml
文件中添加以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
3. 配置数据源
由于需要使用多个数据库节点,因此我们需要配置多个数据源。在 application.properties
文件中添加以下配置:
# 数据源1
spring.datasource.master.url=jdbc:mysql://localhost:3306/db_master?characterEncoding=utf-8
spring.datasource.master.username=root
spring.datasource.master.password=root
spring.datasource.master.driver-class-name=com.mysql.jdbc.Driver
# 数据源2
spring.datasource.slave.url=jdbc:mysql://localhost:3306/db_slave?characterEncoding=utf-8
spring.datasource.slave.username=root
spring.datasource.slave.password=root
spring.datasource.slave.driver-class-name=com.mysql.jdbc.Driver
4. 配置MyBatis-Plus
在application.properties
文件中添加以下配置:
# MyBatis-Plus配置
mybatis-plus.mapper-locations=classpath:mapper/**/*.xml
mybatis-plus.global-config.db-config.id-type=auto
mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0
5. 定义实体类
在com.example.readwritesplit.entity
包中创建一个名为User
的类,在其中定义数据库表user
的字段:
@Data
@TableName("user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private String email;
}
6. 创建Mapper接口
在com.example.readwritesplit.mapper
包中创建一个名为UserMapper
的接口,并继承BaseMapper
接口:
@Repository
public interface UserMapper extends BaseMapper<User> {
}
7. 创建Service接口和实现类
在com.example.readwritesplit.service
包中创建一个名为UserService
的接口,并定义需要的方法:
public interface UserService {
List<User> listAllUsers();
void saveUser(User user);
}
然后在com.example.readwritesplit.service.impl
包中创建一个名为UserServiceImpl
的类,并实现UserService
接口:
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public List<User> listAllUsers() {
return userMapper.selectList(null);
}
@Override
public void saveUser(User user) {
userMapper.insert(user);
}
}
8. 配置读写分离
在com.example.readwritesplit.config
包中创建一个名为DataSourceConfig
的类,用于配置读写数据源:
@Configuration
public class DataSourceConfig {
@Bean(name = "masterDataSource")
@ConfigurationProperties(prefix = "spring.datasource.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "slaveDataSource")
@ConfigurationProperties(prefix = "spring.datasource.slave")
public DataSource slaveDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "routingDataSource")
public DataSource routingDataSource(@Qualifier("masterDataSource") DataSource masterDataSource,
@Qualifier("slaveDataSource") DataSource slaveDataSource) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DatabaseType.MASTER, masterDataSource);
targetDataSources.put(DatabaseType.SLAVE, slaveDataSource);
RoutingDataSource routingDataSource = new RoutingDataSource();
routingDataSource.setDefaultTargetDataSource(masterDataSource);
routingDataSource.setTargetDataSources(targetDataSources);
return routingDataSource;
}
@Bean
public SqlSessionFactory sqlSessionFactory(@Qualifier("routingDataSource") DataSource routingDataSource) throws Exception {
MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
sessionFactory.setDataSource(routingDataSource);
sessionFactory.setTypeAliasesPackage("com.example.readwritesplit.entity");
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
sessionFactory.setMapperLocations(resolver.getResources("classpath:mapper/**/*.xml"));
return sessionFactory.getObject();
}
@Bean
public PlatformTransactionManager transactionManager(@Qualifier("routingDataSource") DataSource routingDataSource) {
return new DataSourceTransactionManager(routingDataSource);
}
}
9. 实现读写分离的动态切换
在com.example.readwritesplit.config
包中创建一个名为RoutingDataSource
的类,用于动态切换数据源:
public class RoutingDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContext.getDatabaseType();
}
}
在com.example.readwritesplit.config
包中创建一个名为DataSourceContext
的类,用于保存当前数据源的信息:
public class DataSourceContext {
private static final ThreadLocal<DatabaseType> CONTEXT = new ThreadLocal<>();
public static void setDatabaseType(DatabaseType type) {
CONTEXT.set(type);
}
public static DatabaseType getDatabaseType() {
return CONTEXT.get();
}
public static void clearDatabaseType() {
CONTEXT.remove();
}
}
在com.example.readwritesplit.config
包中创建一个名为DataSourceAspect
的类,用于在方法执行前根据需要切换数据源:
@Aspect
@Slf4j
@Component
public class DataSourceAspect {
@Pointcut("execution(* com.example.readwritesplit.service.*.*(..))")
public void servicePointCut() {
}
@Before("servicePointCut()")
public void before(JoinPoint joinPoint) {
Class<?> clazz = joinPoint.getTarget().getClass();
DatabaseType databaseType = clazz.isAnnotationPresent(ReadOnly.class) ? DatabaseType.SLAVE : DatabaseType.MASTER;
DataSourceContext.setDatabaseType(databaseType);
}
@After("servicePointCut()")
public void after(JoinPoint joinPoint) {
DataSourceContext.clearDatabaseType();
}
}
添加@ReadOnly
注解到读操作的方法上,示例如下:
@Repository
public interface UserMapper extends BaseMapper<User> {
@ReadOnly // 表示查询操作
@Select("SELECT * FROM user")
List<User> selectList();
}
10. 测试读写分离
在com.example.readwritesplit.controller
包中创建一个名为UserController
的类,用于测试读写分离效果:
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/users")
public List<User> listAllUsers() {
return userService.listAllUsers();
}
@PostMapping("/user")
public void saveUser(@RequestBody User user) {
userService.saveUser(user);
}
}
启动Spring Boot应用程序,然后使用以下URL测试读操作:
GET http://localhost:8080/users
使用以下URL测试写操作:
POST http://localhost:8080/user
Body:
{
"name": "John",
"email": "john@example.com"
}
结论
通过Spring Boot和MyBatis-Plus的配合,我们可以很容易地实现数据库的读写分离。这使得我们可以在高并发的情况下提高数据库的性能和稳定性。希望本文对你有所帮助!
本文来自极简博客,作者:梦里水乡,转载请注明原文链接:Spring Boot MyBatis-Plus实现数据库读写分离