spring 本地多數據源及其事務

一.引言

目前項目中存在一個單體服務操作多個Mysql的情況,歷史方案:項目通過繼承AbstractRoutingDataSource,重寫獲取數據源的方法,再提供一個utils類,實現根據租戶id的動態(tài)數據源的切換。

問題:

1.動態(tài)數據源切換需要手動調用utils類的方法,該方法出現在controller,serviceImpl等各個地方,影響了業(yè)務代碼的邏輯可讀性和清晰性。

2.如果開啟了spring的事務,切換數據源必須使用request_new的事務傳播方式,這導致開啟事務的方法里切換數據源不成功,并且多數據源事務無法保證。

(吐槽:不明白為啥不用dynamic-datasource,非法要老方法)


二.想法和方案

v1版本:

?目標

1.通過注解的方式切換數據源,方法內也可切換

2.支持多數據源事務問題

3.可重復讀

實現方案

增加repository隔離層,增加Dbswitch注解,通過AOP攔截實現對repository的mysql數據庫操作方法進行方法前數據源切換。

不使用spring聲明式事務,改為手動式事務,切換數據源的時候都獲取request_new新事務,保證數據源能切換成功,提供事務改為從隊尾開始鏈式提交,回滾事務改為從隊尾開始鏈式回滾。

現象:

目標1實現,

目標2猜測存在問題:提交事務的順序可能會導致代碼邏輯錯誤,例如 A-update a set a.id = 5,B-update a set a.id = 6

這時候B事務先提交 ,A事務后提交,導致id的數據是5。實際情況是B的sql一直卡主,直至超時,因為A,B是兩個連接操作的是同一行數據,A先操作拿到行鎖,B再操作是等待A釋放鎖,然而A事務是等B操作完才提交,這就導致超時了。

結論

目標都能實現,但方法內如果出現sql資源競爭就會超時異常。

V2版本:

目標

1.通過注解的方式切換數據源,方法內也可切換

2.支持多數據源事務問題

3.可重復讀

4.方法內不存在鎖競爭

實現方案

參考dynamic-datasource,通過拿到connection來實現事務的提交和回滾,不使用spring的事務管理器。

難點:1 如何拿到mybatis的connection,因為如果拿到的sql執(zhí)行connection不一致的話,事務不生效

? ? ? ? ? ? 2 如果實現像spring事務那樣的request,request_new的事務傳播

解答:1.1 剛開始想直接通過拿到datasource,通過getconnection獲取,發(fā)現拿到的是新connection.不可行

? ? ? ? ? ?1.2 查資料想通過spring提供的DataSourceUtils獲取connection,發(fā)現只有開啟spring的事務才能獲取到。不可行

? ? ? ? ? ?1.3 通過創(chuàng)建DbSwitchAbstractRoutingDataSource類繼承AbstractRoutingDataSource,重寫getConnection的方法,通過靜態(tài)代理類ConnectionProxy代理Connection,并創(chuàng)建同一包路徑的DynamicDataSource繼承DbSwitchAbstractRoutingDataSource,從而覆蓋原來的DynamicDataSource,并注入DataSource的bean,這樣mybatis的sql執(zhí)行的connection就可獲取到。并且同一線程內,同一數據源執(zhí)行sql用的conncetion是一致的。這樣就不會出現sql的鎖競爭問題。

結論

目標2未實現,其他目標實現。考慮目前不需要目標二的情況,暫時不實現。

三.總結

目前項目使用的是V2版本,后續(xù)如果需要會加上事務的傳播行為。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容