0


Spring Boot项目优雅实现读写分离

文章目录

在这里插入图片描述

🎉欢迎来到架构设计专栏~Spring Boot项目优雅实现读写分离


  • ☆* o(≧▽≦)o *☆嗨~我是IT·陈寒🍹
  • ✨博客主页:IT·陈寒的博客
  • 🎈该系列文章专栏:架构设计
  • 📜其他专栏:Java学习路线 Java面试技巧 Java实战项目 AIGC人工智能 数据结构学习
  • 🍹文章作者技术和水平有限,如果文中出现错误,希望大家能指正🙏
  • 📜 欢迎大家关注! ❤️

Spring Boot作为一种快速开发框架,广泛应用于Java项目中。在一些大型应用中,数据库的读写分离是提升性能和扩展性的一种重要手段。本文将介绍如何在Spring Boot项目中优雅地实现读写分离,并通过适当的代码插入,详细展开实现步骤,同时进行拓展和分析。
在这里插入图片描述

1. 读写分离简介

读写分离是指在数据库集群中,将数据库的读操作和写操作分别分配到不同的节点上。这样可以充分利用多台服务器的资源,提高系统的并发处理能力。一般来说,写操作相对读操作更为耗时,通过读写分离,可以有效减轻主库的负担。

2. Spring Boot集成MyBatis

首先,我们需要在Spring Boot项目中集成MyBatis,作为数据库访问的ORM框架。可以通过在

pom.xml

文件中添加依赖来引入MyBatis和MyBatis-Spring-Boot-Starter:

<!-- MyBatis依赖 --><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.2.0</version></dependency><!-- 数据库连接池依赖(这里以HikariCP为例) --><dependency><groupId>com.zaxxer</groupId><artifactId>HikariCP</artifactId></dependency>

然后,配置

application.properties

文件,指定数据库连接信息:

# 主库
spring.datasource.master.url=jdbc:mysql://master-host:3306/master_db
spring.datasource.master.username=root
spring.datasource.master.password=root
# 从库
spring.datasource.slave.url=jdbc:mysql://slave-host:3306/slave_db
spring.datasource.slave.username=root
spring.datasource.slave.password=root

3. 配置读写分离数据源

在实现读写分离前,我们需要定义一个数据源路由器,用于根据不同的操作选择不同的数据源。在Spring Boot中,可以通过

AbstractRoutingDataSource

实现这一功能。以下是一个简单的数据源路由器示例:

importorg.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;publicclassRoutingDataSourceextendsAbstractRoutingDataSource{@OverrideprotectedObjectdetermineCurrentLookupKey(){returnDataSourceContextHolder.getDataSourceType();}}

在上述代码中,

determineCurrentLookupKey

方法用于确定当前使用的数据源,

DataSourceContextHolder

是一个自定义的上下文持有类,用于存储当前线程使用的数据源类型。接下来,我们需要在配置类中配置这个数据源路由器:

importorg.springframework.beans.factory.annotation.Qualifier;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.jdbc.datasource.DataSourceTransactionManager;importorg.springframework.transaction.PlatformTransactionManager;importjavax.sql.DataSource;importjava.util.HashMap;importjava.util.Map;@ConfigurationpublicclassDataSourceConfig{@Bean(name ="masterDataSource")@ConfigurationProperties(prefix ="spring.datasource.master")publicDataSourcemasterDataSource(){returnDataSourceBuilder.create().build();}@Bean(name ="slaveDataSource")@ConfigurationProperties(prefix ="spring.datasource.slave")publicDataSourceslaveDataSource(){returnDataSourceBuilder.create().build();}@Bean(name ="routingDataSource")publicRoutingDataSourceroutingDataSource(@Qualifier("masterDataSource")DataSource masterDataSource,@Qualifier("slaveDataSource")DataSource slaveDataSource){RoutingDataSource routingDataSource =newRoutingDataSource();Map<Object,Object> targetDataSources =newHashMap<>();
        targetDataSources.put(DataSourceType.MASTER, masterDataSource);
        targetDataSources.put(DataSourceType.SLAVE, slaveDataSource);
        routingDataSource.setTargetDataSources(targetDataSources);
        routingDataSource.setDefaultTargetDataSource(masterDataSource);return routingDataSource;}@BeanpublicPlatformTransactionManagertransactionManager(DataSource routingDataSource){returnnewDataSourceTransactionManager(routingDataSource);}}

在上述代码中,我们配置了两个数据源,一个用于主库,一个用于从库。然后,通过

routingDataSource

方法创建了一个

RoutingDataSource

实例,将主库和从库加入到数据源路由器中,并设置默认数据源为主库。

4. 定义数据源上下文

接下来,我们需要定义一个数据源上下文类,用于在当前线程中保存和获取当前使用的数据源类型。这个上下文类应该是线程安全的,因为它会在多个线程中被访问。

publicclassDataSourceContextHolder{privatestaticfinalThreadLocal<DataSourceType> contextHolder =newThreadLocal<>();publicstaticvoidsetDataSourceType(DataSourceType dataSourceType){
        contextHolder.set(dataSourceType);}publicstaticDataSourceTypegetDataSourceType(){return contextHolder.get();}publicstaticvoidclearDataSourceType(){
        contextHolder.remove();}}

5. 自定义注解和切面

为了在Service层标注读操作和写操作,我们可以定义两个自定义注解

@Master

@Slave

,并创建一个切面

DataSourceAspect

,通过AOP切入点拦截被这两个注解标记的方法,然后在方法执行前设置数据源类型。

@Aspect@ComponentpublicclassDataSourceAspect{@Before("@annotation(master)")publicvoidsetMasterDataSource(JoinPoint joinPoint,Master master){DataSourceContextHolder.setDataSourceType(DataSourceType.MASTER);}@Before("@annotation(slave)")publicvoidsetSlaveDataSource(JoinPoint joinPoint,Slave slave){DataSourceContextHolder.setDataSourceType(DataSourceType.SLAVE);}@After("@annotation(master) || @annotation(slave)")publicvoidclearDataSourceType(JoinPoint joinPoint,Master master,Slave slave){DataSourceContextHolder.clearDataSourceType();}}

在上述代码中,通过

@Before

注解定义了两个切入点,分别拦截被

@Master

@Slave

注解标记的方法,在方法执行前设置对应的数据源类型。在

@After

注解中清除当前线程的数据源类型。

6. 在Service层使用注解

最后,在Service层需要进行读写分离的方法上使用定义好的注解,标记读操作和写操作。以下是一个示例:

@ServicepublicclassUserService{@AutowiredprivateUserMapper userMapper;@MasterpublicUsergetUserById(int userId){return userMapper.selectById(userId);}@SlavepublicList<User>listAllUsers(){return userMapper.selectAll();}@MasterpublicvoidsaveUser(User user){
        userMapper.insert(user);}}

在上述示例中,

@Master

@Slave

注解分别用于标记读操作和写操作的方法。在实际应用中,根据具体需求和业务场景进行灵活使用。

7. 拓展与分析

7.1 多数据源的选择

上述示例中使用了两个数据源,一个用于主库,一个用于从库。在实际应用中,如果有多个从库,可以在配置类中配置多个从库数据源,然后在数据源路由器中动态选择。

7.2 事务的处理

在涉及到事务的场景中,需要注意对事务的处理。在使用读写分离的情况下,一般将写操作放在事务中,而读操作不放在事务中。因为事务一般需要使用主库,而从库主要用于读取操作,不参与事务的提交与回滚。

7.3 异常处理

在使用读写分离的过程中,可能会遇到主从同步延迟导致的数据不一致问题。因此,在涉及到数据一致性要求较高的业务场景中,需要谨慎使用读写分离,考虑一些其他的解决方案,如数据的多版本控制等。

7.4 动态数据源切换

上述示例中使用AOP切面在方法执行前设置数据源类型。在某些场景中,可能需要在代码中动态切换数据源,这时可以通过编程式的方式设置数据源类型,而不是依赖AOP。

7.5 Spring Boot版本适配

请注意根据使用的Spring Boot版本来选择相应的依赖版本。在示例中,使用的MyBatis版本是2.2.0,如果使用的是较新的Spring Boot版本,建议查阅官方文档或相关依赖库的最新版本。

在这里插入图片描述

通过上述步骤,我们完成了Spring Boot项目中读写分离的优雅实现。通过合理的代码插入,详细展开了每个步骤的实现,并对一些拓展和分析进行了说明。希望这篇文章对正在进行数据库优化的开发者有所帮助。


🧸结尾 ❤️ 感谢您的支持和鼓励! 😊🙏
📜您可能感兴趣的内容:

  • 【Java面试技巧】Java面试八股文 - 掌握面试必备知识(目录篇)
  • 【Java学习路线】2023年完整版Java学习路线图
  • 【AIGC人工智能】Chat GPT是什么,初学者怎么使用Chat GPT,需要注意些什么
  • 【Java实战项目】SpringBoot+SSM实战:打造高效便捷的企业级Java外卖订购系统
  • 【数据结构学习】从零起步:学习数据结构的完整路径

在这里插入图片描述

标签: spring boot 后端 java

本文转载自: https://blog.csdn.net/qq_43546721/article/details/134395015
版权归原作者 IT·陈寒 所有, 如有侵权,请联系我们删除。

“Spring Boot项目优雅实现读写分离”的评论:

还没有评论