在SpringBoot項(xiàng)目中實(shí)現(xiàn)讀寫(xiě)分離的流程步驟
1. 讀寫(xiě)分離簡(jiǎn)介
讀寫(xiě)分離是指在數(shù)據(jù)庫(kù)集群中,將數(shù)據(jù)庫(kù)的讀操作和寫(xiě)操作分別分配到不同的節(jié)點(diǎn)上。這樣可以充分利用多臺(tái)服務(wù)器的資源,提高系統(tǒng)的并發(fā)處理能力。一般來(lái)說(shuō),寫(xiě)操作相對(duì)讀操作更為耗時(shí),通過(guò)讀寫(xiě)分離,可以有效減輕主庫(kù)的負(fù)擔(dān)。
2. Spring Boot集成MyBatis
首先,我們需要在Spring Boot項(xiàng)目中集成MyBatis,作為數(shù)據(jù)庫(kù)訪問(wèn)的ORM框架??梢酝ㄟ^(guò)在pom.xml
文件中添加依賴來(lái)引入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> <!-- 數(shù)據(jù)庫(kù)連接池依賴(這里以HikariCP為例) --> <dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> </dependency>
然后,配置application.properties
文件,指定數(shù)據(jù)庫(kù)連接信息:
# 主庫(kù) spring.datasource.master.url=jdbc:mysql://master-host:3306/master_db spring.datasource.master.username=root spring.datasource.master.password=root # 從庫(kù) spring.datasource.slave.url=jdbc:mysql://slave-host:3306/slave_db spring.datasource.slave.username=root spring.datasource.slave.password=root
3. 配置讀寫(xiě)分離數(shù)據(jù)源
在實(shí)現(xiàn)讀寫(xiě)分離前,我們需要定義一個(gè)數(shù)據(jù)源路由器,用于根據(jù)不同的操作選擇不同的數(shù)據(jù)源。在Spring Boot中,可以通過(guò)AbstractRoutingDataSource
實(shí)現(xiàn)這一功能。以下是一個(gè)簡(jiǎn)單的數(shù)據(jù)源路由器示例:
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class RoutingDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getDataSourceType(); } }
在上述代碼中,determineCurrentLookupKey
方法用于確定當(dāng)前使用的數(shù)據(jù)源,DataSourceContextHolder
是一個(gè)自定義的上下文持有類,用于存儲(chǔ)當(dāng)前線程使用的數(shù)據(jù)源類型。接下來(lái),我們需要在配置類中配置這個(gè)數(shù)據(jù)源路由器:
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; @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 RoutingDataSource routingDataSource( @Qualifier("masterDataSource") DataSource masterDataSource, @Qualifier("slaveDataSource") DataSource slaveDataSource) { RoutingDataSource routingDataSource = new RoutingDataSource(); Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put(DataSourceType.MASTER, masterDataSource); targetDataSources.put(DataSourceType.SLAVE, slaveDataSource); routingDataSource.setTargetDataSources(targetDataSources); routingDataSource.setDefaultTargetDataSource(masterDataSource); return routingDataSource; } @Bean public PlatformTransactionManager transactionManager(DataSource routingDataSource) { return new DataSourceTransactionManager(routingDataSource); } }
在上述代碼中,我們配置了兩個(gè)數(shù)據(jù)源,一個(gè)用于主庫(kù),一個(gè)用于從庫(kù)。然后,通過(guò)routingDataSource
方法創(chuàng)建了一個(gè)RoutingDataSource
實(shí)例,將主庫(kù)和從庫(kù)加入到數(shù)據(jù)源路由器中,并設(shè)置默認(rèn)數(shù)據(jù)源為主庫(kù)。
4. 定義數(shù)據(jù)源上下文
接下來(lái),我們需要定義一個(gè)數(shù)據(jù)源上下文類,用于在當(dāng)前線程中保存和獲取當(dāng)前使用的數(shù)據(jù)源類型。這個(gè)上下文類應(yīng)該是線程安全的,因?yàn)樗鼤?huì)在多個(gè)線程中被訪問(wèn)。
public class DataSourceContextHolder { private static final ThreadLocal<DataSourceType> contextHolder = new ThreadLocal<>(); public static void setDataSourceType(DataSourceType dataSourceType) { contextHolder.set(dataSourceType); } public static DataSourceType getDataSourceType() { return contextHolder.get(); } public static void clearDataSourceType() { contextHolder.remove(); } }
5. 自定義注解和切面
為了在Service層標(biāo)注讀操作和寫(xiě)操作,我們可以定義兩個(gè)自定義注解@Master
和@Slave
,并創(chuàng)建一個(gè)切面DataSourceAspect
,通過(guò)AOP切入點(diǎn)攔截被這兩個(gè)注解標(biāo)記的方法,然后在方法執(zhí)行前設(shè)置數(shù)據(jù)源類型。
@Aspect @Component public class DataSourceAspect { @Before("@annotation(master)") public void setMasterDataSource(JoinPoint joinPoint, Master master) { DataSource ContextHolder.setDataSourceType(DataSourceType.MASTER); } @Before("@annotation(slave)") public void setSlaveDataSource(JoinPoint joinPoint, Slave slave) { DataSourceContextHolder.setDataSourceType(DataSourceType.SLAVE); } @After("@annotation(master) || @annotation(slave)") public void clearDataSourceType(JoinPoint joinPoint, Master master, Slave slave) { DataSourceContextHolder.clearDataSourceType(); } }
在上述代碼中,通過(guò)@Before
注解定義了兩個(gè)切入點(diǎn),分別攔截被@Master
和@Slave
注解標(biāo)記的方法,在方法執(zhí)行前設(shè)置對(duì)應(yīng)的數(shù)據(jù)源類型。在@After
注解中清除當(dāng)前線程的數(shù)據(jù)源類型。
6. 在Service層使用注解
最后,在Service層需要進(jìn)行讀寫(xiě)分離的方法上使用定義好的注解,標(biāo)記讀操作和寫(xiě)操作。以下是一個(gè)示例:
@Service public class UserService { @Autowired private UserMapper userMapper; @Master public User getUserById(int userId) { return userMapper.selectById(userId); } @Slave public List<User> listAllUsers() { return userMapper.selectAll(); } @Master public void saveUser(User user) { userMapper.insert(user); } }
在上述示例中,@Master
和@Slave
注解分別用于標(biāo)記讀操作和寫(xiě)操作的方法。在實(shí)際應(yīng)用中,根據(jù)具體需求和業(yè)務(wù)場(chǎng)景進(jìn)行靈活使用。
7. 拓展與分析
7.1 多數(shù)據(jù)源的選擇
上述示例中使用了兩個(gè)數(shù)據(jù)源,一個(gè)用于主庫(kù),一個(gè)用于從庫(kù)。在實(shí)際應(yīng)用中,如果有多個(gè)從庫(kù),可以在配置類中配置多個(gè)從庫(kù)數(shù)據(jù)源,然后在數(shù)據(jù)源路由器中動(dòng)態(tài)選擇。
7.2 事務(wù)的處理
在涉及到事務(wù)的場(chǎng)景中,需要注意對(duì)事務(wù)的處理。在使用讀寫(xiě)分離的情況下,一般將寫(xiě)操作放在事務(wù)中,而讀操作不放在事務(wù)中。因?yàn)槭聞?wù)一般需要使用主庫(kù),而從庫(kù)主要用于讀取操作,不參與事務(wù)的提交與回滾。
7.3 異常處理
在使用讀寫(xiě)分離的過(guò)程中,可能會(huì)遇到主從同步延遲導(dǎo)致的數(shù)據(jù)不一致問(wèn)題。因此,在涉及到數(shù)據(jù)一致性要求較高的業(yè)務(wù)場(chǎng)景中,需要謹(jǐn)慎使用讀寫(xiě)分離,考慮一些其他的解決方案,如數(shù)據(jù)的多版本控制等。
7.4 動(dòng)態(tài)數(shù)據(jù)源切換
上述示例中使用AOP切面在方法執(zhí)行前設(shè)置數(shù)據(jù)源類型。在某些場(chǎng)景中,可能需要在代碼中動(dòng)態(tài)切換數(shù)據(jù)源,這時(shí)可以通過(guò)編程式的方式設(shè)置數(shù)據(jù)源類型,而不是依賴AOP。
7.5 Spring Boot版本適配
請(qǐng)注意根據(jù)使用的Spring Boot版本來(lái)選擇相應(yīng)的依賴版本。在示例中,使用的MyBatis版本是2.2.0,如果使用的是較新的Spring Boot版本,建議查閱官方文檔或相關(guān)依賴庫(kù)的最新版本。
通過(guò)上述步驟,我們完成了Spring Boot項(xiàng)目中讀寫(xiě)分離的優(yōu)雅實(shí)現(xiàn)。通過(guò)合理的代碼插入,詳細(xì)展開(kāi)了每個(gè)步驟的實(shí)現(xiàn),并對(duì)一些拓展和分析進(jìn)行了說(shuō)明。希望這篇文章對(duì)正在進(jìn)行數(shù)據(jù)庫(kù)優(yōu)化的開(kāi)發(fā)者有所幫助。
以上就是在SpringBoot項(xiàng)目中實(shí)現(xiàn)讀寫(xiě)分離的流程步驟的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot讀寫(xiě)分離的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- 最新MySql8.27主從復(fù)制及SpringBoot項(xiàng)目中的讀寫(xiě)分離實(shí)戰(zhàn)教程
- SpringBoot項(xiàng)目中如何實(shí)現(xiàn)MySQL讀寫(xiě)分離詳解
- SpringBoot多數(shù)據(jù)源讀寫(xiě)分離的自定義配置問(wèn)題及解決方法
- SpringBoot詳解如何實(shí)現(xiàn)讀寫(xiě)分離
- SpringBoot+MyBatis+AOP實(shí)現(xiàn)讀寫(xiě)分離的示例代碼
- SpringBoot整合sharding-jdbc實(shí)現(xiàn)分庫(kù)分表與讀寫(xiě)分離的示例
相關(guān)文章
聊聊Spring循環(huán)依賴三級(jí)緩存是否可以減少為二級(jí)緩存的情況
這篇文章主要介紹了聊聊Spring循環(huán)依賴三級(jí)緩存是否可以減少為二級(jí)緩存的情況,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-02-02VsCode搭建Java開(kāi)發(fā)環(huán)境的方法
這篇文章主要介紹了VsCode搭建Java開(kāi)發(fā)環(huán)境的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-11-11Java中保證多線程間的數(shù)據(jù)共享的方法詳解
這篇文章詳解的發(fā)給大家介紹了Java中是如何保證多線程間的數(shù)據(jù)共享的,文中通過(guò)圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2023-11-11解決springdataJPA對(duì)原生sql支持的問(wèn)題
這篇文章主要介紹了解決springdataJPA對(duì)原生sql支持的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06Mybatis 多對(duì)一查詢的實(shí)現(xiàn)方法
這篇文章主要介紹了Mybatis 多對(duì)一查詢,本文通過(guò)場(chǎng)景分析示例代碼相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-02-02Java.lang.OutOfMemoryError: GC overhead limit
本文主要介紹了Java.lang.OutOfMemoryError: GC overhead limit exceeded錯(cuò)誤的解決,錯(cuò)誤是由于堆空間不足導(dǎo)致GC頻繁運(yùn)行,從而引起的,下面就來(lái)介紹一下解決方法2025-03-03SpringMVC實(shí)現(xiàn)簡(jiǎn)單跳轉(zhuǎn)方法(專題)
這篇文章主要介紹了SpringMVC實(shí)現(xiàn)簡(jiǎn)單跳轉(zhuǎn)方法(專題),詳細(xì)的介紹了SpringMVC跳轉(zhuǎn)的幾種方法,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2018-03-03