SpringBoot 項目實現讀寫分離

SpringBoot 項目實現讀寫分離

一、讀寫分離介紹
當使用Spring Boot開發數據庫應用時,讀寫分離是一種常見的優化策略。讀寫分離將讀操作和寫操作分別分配給不同的數據庫實例,以提高系統的吞吐量和性能。
讀寫分離實現主要是通過動態數據源功能實現的,動態數據源是一種通過在運行時動態切換數據庫連接的機制。它允許應用程序根據不同的條件或配置選擇不同的數據源,以實現更靈活和可擴展的數據庫訪問。

二、實現讀寫分離-基礎

  1. 配置主數據庫和從數據庫的連接信息
# 主庫配置
spring.datasource.master.jdbc-url=jdbc:mysql://ip:port/master?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false
spring.datasource.master.username=master
spring.datasource.master.password=123456
spring.datasource.master.driver-class-name=com.mysql.jdbc.Driver# 從庫配置
spring.datasource.slave.jdbc-url=jdbc:mysql://ip:port/slave?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false
spring.datasource.slave.username=slave
spring.datasource.slave.password=123456
spring.datasource.slave.driver-class-name=com.mysql.jdbc.Driver 
  1. 創建主數據庫和從數據庫的數據源配置類
    通過不同的條件限制和配置文件前綴可以完成不同數據源的創建工作,不止是主從也可以是多個不同的數據庫
    主庫數據源配置
@Configuration
@ConditionalOnProperty("spring.datasource.master.jdbc-url")
public class MasterDataSourceConfiguration {@Bean("masterDataSource")@ConfigurationProperties(prefix = "spring.datasource.master")public DataSource masterDataSource() {return DataSourceBuilder.create().build();}
}

從庫數據源配置

@Configuration
@ConditionalOnProperty("spring.datasource.slave.jdbc-url")
public class SlaveDataSourceConfiguration {@Bean("slaveDataSource")@ConfigurationProperties(prefix = "spring.datasource.slave")public DataSource slaveDataSource() {return DataSourceBuilder.create().build();}
}
  1. 創建主從數據源枚舉
public enum DataSourceTypeEnum {/*** 主庫*/MASTER,/*** 從庫*/SLAVE,;}
  1. 創建動態路由數據源
    這兒做了一個開關,可以控制讀寫分離的開啟和關閉工作,可以將操作全部切換到主庫進行。然后根據上下文中的數據源類型來返回不同的數據源類型枚舉
@Slf4j
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {@Value("${DB_RW_SEPARATE_SWITCH:false}")private boolean dbRwSeparateSwitch;@Overrideprotected Object determineCurrentLookupKey() {if(dbRwSeparateSwitch && DataSourceTypeEnum.SLAVE.equals(DataSourceContextHolder.getDataSourceType())) {log.info("DynamicRoutingDataSource 切換數據源到從庫");return DataSourceTypeEnum.SLAVE;}log.info("DynamicRoutingDataSource 切換數據源到主庫");// 根據需要指定當前使用的數據源,這里可以使用ThreadLocal或其他方式來決定使用主庫還是從庫return DataSourceTypeEnum.MASTER;}
}
  1. 創建動態數據源配置類
    將主數據庫和從數據庫的數據源添加到動態數據源中,并可以通過枚舉創建一個數據源 map,這樣就可以通過上面的路由返回的枚舉來切換數據源
@Configuration
@ConditionalOnProperty("spring.datasource.master.jdbc-url")
public class DynamicDataSourceConfiguration {@Bean("dataSource")@Primarypublic DataSource dynamicDataSource(DataSource masterDataSource, DataSource slaveDataSource) {Map<Object, Object> targetDataSources = new HashMap<>();targetDataSources.put(DataSourceTypeEnum.MASTER, masterDataSource);targetDataSources.put(DataSourceTypeEnum.SLAVE, slaveDataSource);DynamicRoutingDataSource dynamicDataSource = new DynamicRoutingDataSource();dynamicDataSource.setTargetDataSources(targetDataSources);dynamicDataSource.setDefaultTargetDataSource(masterDataSource);return dynamicDataSource;}
}
  1. 創建DatasourceContextHolder類使用ThreadLocal存儲當前線程的數據源類型
    注意這兒有個潛在風險就是創建新的線程時會導致 ThreadLocal 中的數據無法正確讀取,如果涉及到在開啟新線程可以使用 TransmittableThreadLocal 來進行父子線程數據的同步,git 地址:https://github.com/alibaba/transmittable-thread-local
public class DataSourceContextHolder {private static final ThreadLocal<DataSourceTypeEnum> contextHolder = new ThreadLocal<>();public static void setDataSourceType(DataSourceTypeEnum dataSourceType) {contextHolder.set(dataSourceType);}public static DataSourceTypeEnum getDataSourceType() {return contextHolder.get();}public static void clearDataSourceType() {contextHolder.remove();}
}
  1. 創建自定義注解,用于標記主和從數據源
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MasterDataSource {
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface SlaveDataSource {
}
  1. 創建切面類,攔截數據庫操作,并根據注解設置切換數據源參數
@Aspect
@Component
public class DataSourceAspect {@Before("@annotation(xxx.MasterDataSource)")public void setMasterDataSource(JoinPoint joinPoint) {DataSourceContextHolder.setDataSourceType(DataSourceTypeEnum.MASTER);}@Before("@annotation(xxx.SlaveDataSource)")public void setSlaveDataSource(JoinPoint joinPoint) {DataSourceContextHolder.setDataSourceType(DataSourceTypeEnum.SLAVE);}@After("@annotation(xxx.MasterDataSource) || @annotation(xxx.SlaveDataSource)")public void clearDataSource(JoinPoint joinPoint) {DataSourceContextHolder.clearDataSourceType();}
}
  1. 在Service層的方法上使用自定義注解標記查詢數據源
@Service
public class TestService {@Autowiredprivate TestDao testDao;@SlaveDataSourcepublic Test test() {return testDao.queryByPrimaryKey(11L);}
}
  1. 排除掉數據源自動配置類
    如果不排除自動配置類會導致初始化多個 dataSource 對象導致出現問題
    SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})

三、實現讀寫分離-進階

  1. 使用鏈接池,以Hikari為例
    修改鏈接配置,加入鏈接池相關配置即可
# 主庫配置
spring.datasource.master.jdbc-url=jdbc:mysql://ip:port/master?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false
spring.datasource.master.username=master
spring.datasource.master.password=123456
spring.datasource.master.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.master.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.master.hikari.name=master
spring.datasource.master.hikari.minimum-idle=5
spring.datasource.master.hikari.idle-timeout=30
spring.datasource.master.hikari.maximum-pool-size=10
spring.datasource.master.hikari.auto-commit=true
spring.datasource.master.hikari.pool-name=DatebookHikariCP
spring.datasource.master.hikari.max-lifetime=1800000
spring.datasource.master.hikari.connection-timeout=30000
spring.datasource.master.hikari.connection-test-query=SELECT 1# 從庫配置
spring.datasource.slave.jdbc-url=jdbc:mysql://ip:port/slave?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false
spring.datasource.slave.username=root
spring.datasource.slave.password=123456
spring.datasource.slave.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.slave.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.slave.hikari.name=master
spring.datasource.slave.hikari.minimum-idle=5
spring.datasource.slave.hikari.idle-timeout=30
spring.datasource.slave.hikari.maximum-pool-size=10
spring.datasource.slave.hikari.auto-commit=true
spring.datasource.slave.hikari.pool-name=DatebookHikariCP
spring.datasource.slave.hikari.max-lifetime=1800000
spring.datasource.slave.hikari.connection-timeout=30000
spring.datasource.slave.hikari.connection-test-query=SELECT 1
  1. 集成 mybatis 并在寫入時強制切換到主庫
    不需要做任何配置,正常集成 mybatis 即可使用讀寫分離功能
    可以通過 mybatis 的攔截器在寫入操作時強制切換到主庫
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
})
@Component
public class WriteInterceptor implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {// 獲取 SQL 類型DataSourceTypeEnum dataSourceType = DataSourceContextHolder.getDataSourceType();if(DataSourceTypeEnum.SLAVE.equals(dataSourceType)) {DataSourceContextHolder.setDataSourceType(DataSourceTypeEnum.MASTER);}try {// 執行 SQLreturn invocation.proceed();} finally {// 恢復數據源  考慮到寫入后可能會反查,后續都走主庫// DataSourceContextHolder.setDataSourceType(dataSourceType);}}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/166006.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/166006.shtml
英文地址,請注明出處:http://en.pswp.cn/news/166006.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

我的虛擬人物介紹

背景 大家好&#xff0c;我是小欣&#xff0c;是這個博客的虛擬助手。在這里&#xff0c;我將為大家提供各種有趣、實用、甚至是意想不到的信息。作為一個年輕的語言模型&#xff0c;我的目標是為你們呈現出最有趣和有深度的內容。 我喜歡與大家分享知識、解答問題&#xff0…

AI“胡說八道”?怎么解?

原創 | 文 BFT機器人 01 引言 近年來&#xff0c;人工智能產業迅猛發展&#xff0c;大型語言模型GPT-4發展勢頭強勁&#xff0c;OpenAI推出ChatGPT、微軟推出Bing、馬斯克推出“最好的聊天機器人Grok”……科技巨頭紛紛入局AI領域&#xff0c;引入人工智能作為辦公工具的行業…

合封芯片未來趨勢如何?合封優勢能否體現?

芯片已經成為現代電子設備的核心組件。為了提高系統的性能、穩定性和功耗效率&#xff0c;一種先進的芯片封裝技術——合封芯片應運而生。 合封芯片作為一種先進的芯片封裝技術&#xff0c;合封芯片是一種將多個芯片&#xff08;多樣選擇&#xff09;或不同的功能的電子元器件…

AIGC專題報告:生成式人工智能用例匯編

今天分享的是AIGC系列深度研究報告&#xff1a;《AIGC專題報告&#xff1a;生成式人工智能用例匯編》。 &#xff08;報告出品方&#xff1a;德勤&#xff09; 報告共計&#xff1a;16頁 生成式人工智能&#xff08;AI&#xff09;的興起 生成式AI給人類文明創造了無限的可…

文心一言 VS 訊飛星火 VS chatgpt (142)-- 算法導論12.1 2題

二、用go語言&#xff0c;二叉搜索樹性質與最小堆性質(見 6.1 節)之間有什么不同?能使用最小堆性質在 O(n)時間內按序輸出一棵有 n 個結點樹的關鍵字嗎?可以的話&#xff0c;請說明如何做&#xff0c;否則解釋理由。 文心一言&#xff1a; 在二叉搜索樹&#xff08;Binary …

如何更有效地進行薪酬調查?

薪酬調查是企業管理中非常重要的一環&#xff0c;它不僅能夠幫助企業了解員工的薪酬情況&#xff0c;還可以為企業提供合理的薪酬標準&#xff0c;從而吸引和留住優秀人才。然而&#xff0c;進行薪酬調查并不是一項簡單的任務&#xff0c;需要有一定的方法和技巧&#xff0c;許…

ruoyi 若依框架采用第三方登錄

在項目中&#xff0c;前后端分離的若依項目&#xff0c;需要通過統一認證&#xff0c;或者是第三方協帶認證信息跳轉到本系統的指定頁面。需要前后端都做相應的改造&#xff0c;由于第一次實現時已過了很久&#xff0c;再次重寫時&#xff0c;發現還是搞了很長時間&#xff0c;…

PasteNow for mac剪貼板工具

PasteNow 是一款簡單易用的剪貼板管理工具&#xff0c;可幫助用戶快速存儲和管理剪貼板上的文本和圖片內容。用戶可以使用 PasteNow 軟件快速將文本內容保存到不同的筆記或頁面中&#xff0c;也可以方便地將剪貼板上的圖片保存到本地或分享給其他應用程序。 此外&#xff0c;P…

如何進行合理的股權激勵?

股權激勵是企業激勵員工和管理層的一種重要手段&#xff0c;通過向員工提供股權&#xff0c;可以將他們與企業利益緊密聯系在一起&#xff0c;激發員工的積極性和創造力&#xff0c;提高公司的績效和競爭力。然而&#xff0c;要實施合理的股權激勵&#xff0c;需要考慮多個因素…

vue 通過ref調用router-view子組件的方法

由于用的vue2.7版本&#xff0c;但用了vue3 setup的語法&#xff1b; 注意&#xff1a;是vue2的template結構&#xff0c;vue3的setup語法&#xff1b;非這種情況需要舉一反三。 處理方案&#xff1a; 1、對router-view加上ref template修改 直接對router-view加上ref&#x…

金蝶云星空和四化智造MES(WEB)單據接口對接

金蝶云星空和四化智造MES&#xff08;WEB&#xff09;單據接口對接 對接系統&#xff1a;四化智造MES&#xff08;WEB&#xff09; MES系統是集成生產管理、品質管理、設備管理、BI數據中心、庫存管理、工時管理、數據采集、看板管理等為一體的綜合性生產管理系統。通過強調制造…

wagtail-安裝配置

系列文章目錄 文章目錄 系列文章目錄安裝虛擬環境安裝wagtail查看安裝后的包 創建wagtail項目安裝依賴遷移創建超級用戶運行項目 安裝虛擬環境 https://blog.csdn.net/gsl371/article/details/117917857 安裝wagtail (wagenv) C:\djproject\wagprj>pip list Package V…

淘寶88vip退費問題

前一段時間&#xff0c;雙十一活動&#xff0c;想著開個淘寶的88vip領卷買東西會實惠一點&#xff0c;另外&#xff0c;它自帶的權益也不錯&#xff0c;有餓了嗎、網易云、優酷或者芒果的。但是到了當天發現&#xff0c;一個vip的卷也用不了&#xff0c;頓感什么惱火&#xff0…

synchronized的輕量級鎖居然不會自旋?

《Java并發編程的藝術》中說到「如果失敗&#xff0c;表示其他線程競爭鎖&#xff0c;當前線程便嘗試使用自旋來獲取鎖」&#xff0c;并且下文所配的流程圖中明確表示自旋失敗后才會升級為重量級鎖&#xff0c;但《深入理解Java虛擬機》又說「如果出現兩條以上的線程爭用同一個…

超聲波雪深傳感器冬季里的科技魔法

在冬季的某個清晨&#xff0c;當你打開大門&#xff0c;被厚厚的積雪覆蓋的大地映入眼簾&#xff0c;你是否曾想過&#xff0c;這片雪地的深度是多少&#xff1f;它又如何影響著我們的生活和環境&#xff1f;今天&#xff0c;我們將為你揭開這個謎團&#xff0c;介紹一款神秘的…

眼鏡清洗機原理是怎么樣的?2023年眼鏡清洗機推薦

在日常生活中有許多小伙伴是因為看太多書或者是看太多電子產品導致近視佩戴上了眼鏡&#xff0c;畢竟眼鏡佩戴上后就再也離不開它了&#xff0c;像日常佩戴的眼鏡上會積累非常多污垢以及堆積細菌&#xff0c;而我們手動清洗眼鏡時不能除菌也不能清潔到縫隙中&#xff0c;像眼鏡…

thingsboard的WebSocket API的使用

1、參考文檔 Working with telemetry data | ThingsBoard Community Edition 2、訂閱的命令 我們需要訂閱不同的數據,那么該如何來填寫參數呢,你需要參考后端代碼 TelemetryPluginCmdsWrapper 以及訂閱返回的結果參考類:TelemetrySubscriptionUpdate 鏈接地址: https:/…

error: ‘for‘ loop initial declarations are only allowed in C99 or C11 mode

在使用for循環時&#xff0c;在循環內定義變量&#xff0c;出現如下錯誤 [Error] ‘for’ loop initial declarations are only allowed in C99 or C11 mode [Note] use option -stdc99&#xff0c;-stdgnu99&#xff0c;-stdc11 or-stdgnu11 to compile your code 出現這個錯誤…

使用Pytorch從零開始構建GRU

門控循環單元 (GRU) 是 LSTM 的更新版本。讓我們揭開這個網絡的面紗并探索這兩個兄弟姐妹之間的差異。 您聽說過 GRU 嗎&#xff1f;門控循環單元&#xff08;GRU&#xff09;是更流行的長短期記憶&#xff08;LSTM&#xff09;網絡的弟弟&#xff0c;也是循環神經網絡&#x…

極智AI | LLM大模型部署框架之OpenLLM

歡迎關注我的公眾號 [極智視界],獲取我的更多經驗分享 大家好,我是極智視界,本文來介紹一下 LLM大模型部署框架之OpenLLM。 邀您加入我的知識星球「極智視界」,星球內有超多好玩的項目實戰源碼下載,鏈接:https://t.zsxq.com/0aiNxERDq 由于 LLM 大模型在模型結構、模型規…