EntityFramework Core进行读写分离最佳实践方式,了解一下?
本來(lái)打算寫ASP.NET Core MVC基礎(chǔ)系列內(nèi)容,博客評(píng)論有園友提出講講讀寫分離,這個(gè)問題提的好。
大多數(shù)情況下,對(duì)于園友在評(píng)論中提出的問題,如果是值得深究或者大多數(shù)同行比較關(guān)注的問題我都會(huì)私下去看看,然后進(jìn)行對(duì)應(yīng)解答。
若有敘述不當(dāng)之處,還請(qǐng)海涵。我們稍微過(guò)一下事務(wù),本文略長(zhǎng),請(qǐng)耐心閱讀。
什么是事務(wù)呢?有關(guān)事務(wù)詳解可參看我寫的SQL Server基礎(chǔ)系列,我們可歸結(jié)為一句話:多個(gè)提交要么全部成功,要么全部失敗即同生共死,沒有臨陣脫逃者。
那么問題來(lái)了,用了事務(wù)有什么作用或者說(shuō)有什么優(yōu)點(diǎn)呢?事務(wù)允許我們將相關(guān)操作組合打包,以確保應(yīng)用程序數(shù)據(jù)的一致性。
那么使用事務(wù)又有何缺點(diǎn)呢?使用事務(wù)雖然確保了數(shù)據(jù)一致性等等,但是會(huì)影響性能,可能會(huì)造成死鎖。
那么問題又來(lái)了,既然有其優(yōu)缺點(diǎn),那么我們是否可以手寫邏輯實(shí)現(xiàn)數(shù)據(jù)一致性呢?當(dāng)然可以,我們可以模擬事務(wù)回滾、提交的效果,但是這也無(wú)法百分百保證。
調(diào)用SaveChanges方法是否在事務(wù)中呢?
首先我們?cè)诳刂婆_(tái)中進(jìn)行如下數(shù)據(jù)添加,然后添加日志打印。
我們通過(guò)打印日志得知在調(diào)用SaveChanges方法時(shí)則包含在事務(wù)中進(jìn)行提交,所以請(qǐng)那些可在項(xiàng)目中用到多表添加擔(dān)心出現(xiàn)問題就加上了如下開啟事務(wù),這很顯然是多此一舉。
看到如上日志信息還不是更加確定是不是,我們?cè)賮?lái)看看在上下文中的context.Database.AutoTransactionsEnabled 方法,詳細(xì)解釋如下:
通過(guò)AutoTransactionsEnabled方法解釋得知:其默認(rèn)值為True,也就意味著當(dāng)調(diào)用SaveChanges方法將使用事務(wù)性提交。當(dāng)然我們可以在上下文構(gòu)造函數(shù)中設(shè)置是否全局禁用事務(wù),如下:
在EF Core中我們什么時(shí)候會(huì)用到事務(wù)呢?如果是單一上下文,單一數(shù)據(jù)庫(kù),那么事務(wù)跟我們沒啥關(guān)系,壓根不用管事務(wù)。如果是在單一數(shù)據(jù)庫(kù)使用多個(gè)上下文(跨上下文)或者多個(gè)數(shù)據(jù)庫(kù),這個(gè)時(shí)候事務(wù)就閃亮登場(chǎng)了。
比如對(duì)于電商中的商品、購(gòu)物車、訂單管理、支付、物流,我們完全可以實(shí)例化五個(gè)不同的上下文,此時(shí)將涉及到跨上下文操作使用事務(wù)保持?jǐn)?shù)據(jù)一致性,當(dāng)然這是針對(duì)在同一關(guān)系數(shù)據(jù)庫(kù)中。或者是實(shí)例化同一上下文多次來(lái)使用事務(wù)保持?jǐn)?shù)據(jù)一致性。
可以參看官網(wǎng)的介紹《https://docs.microsoft.com/en-us/ef/core/saving/transactions》,沒什么看頭,都是針對(duì)同一數(shù)據(jù)庫(kù)操作,無(wú)非還是我所說(shuō)的跨上下文、使用上下文結(jié)合底層DbConnection來(lái)使用事務(wù)共享連接等等?
稍微大一點(diǎn)的看點(diǎn)則是在EF Core 2.1中引入了System.Transactions,可指定隔離級(jí)別以及使用ambient transactions(查資料作用是存在多個(gè)事務(wù),事務(wù)之間存在連接,如此一來(lái)將顯得整個(gè)作用域非常冗長(zhǎng),通過(guò)使用此事務(wù)則在特定范圍內(nèi),所有連接都將包含在該事務(wù)中),在此就不占用篇幅介紹了。
和大家一樣我們最關(guān)心的是分布式事務(wù),也就是使用不同上下文針對(duì)多個(gè)數(shù)據(jù)庫(kù),但是遺憾的是直到EF Core 2.1還不支持分布式事務(wù),因?yàn)?NET Core中相關(guān)APi也還不完善,繼續(xù)等待吧。
隨著流量的進(jìn)入,數(shù)據(jù)庫(kù)將承受不可抗拒的壓力,單一數(shù)據(jù)庫(kù)將不再適用,這都是隨著項(xiàng)目的演變所帶來(lái)架構(gòu)的迭代改變,這個(gè)時(shí)候就涉及到分庫(kù),對(duì)于查詢的數(shù)據(jù)單獨(dú)作為一個(gè)數(shù)據(jù)庫(kù),作為數(shù)據(jù)的更改也單獨(dú)用一個(gè)數(shù)據(jù)庫(kù),再結(jié)合那些什么負(fù)載均衡等等,數(shù)據(jù)庫(kù)壓力也就減弱了許多。
只作查詢的數(shù)據(jù)庫(kù)我們稱之為從數(shù)據(jù)庫(kù),對(duì)于數(shù)據(jù)庫(kù)更改的數(shù)據(jù)庫(kù)稱之為主數(shù)據(jù)庫(kù),主-從數(shù)據(jù)庫(kù)(Master-Slave)數(shù)據(jù)的同步方式也有很多。
雖然我也沒接觸過(guò),我們就利用SQL Server中的復(fù)制進(jìn)行發(fā)布-訂閱來(lái)模擬演示還是可以的。我們來(lái)看看.NET Core Web應(yīng)用程序如何實(shí)現(xiàn)讀寫分離。
額外加一句,項(xiàng)目中我也未用到,都是我私下的研究,方案行不行,合不合理可以一起探討。我們創(chuàng)建了兩個(gè)Demo數(shù)據(jù)庫(kù),如下:
我們將Demo1作為主數(shù)據(jù)庫(kù),Demo2作為從數(shù)據(jù)庫(kù),接下來(lái)用一張動(dòng)態(tài)圖演示創(chuàng)建復(fù)制發(fā)布-訂閱(每隔10秒發(fā)布一次)。
我們給出Demo1上下文,Demo2和其一樣,按照正常做法接下來(lái)我們應(yīng)該在.NET Core Web應(yīng)用程序中注入Demo1和Demo2上下文,如下:
然后我們創(chuàng)建Demo控制器,通過(guò)Demo1上下文添加數(shù)據(jù),Demo2上下文讀取數(shù)據(jù),如下:
我們看到通過(guò)Demo1上下文添加數(shù)據(jù)后重定向到Demo2上下文查詢到的列表頁(yè)面,到了10秒自動(dòng)同步到Demo2數(shù)據(jù)庫(kù),通過(guò)刷新可以看到數(shù)據(jù)顯示。
雖然結(jié)果如我們所期望,但是實(shí)現(xiàn)的路徑卻令我們不是那么如意,因?yàn)樗脤?shí)體都是一樣的,只是說(shuō)所連接數(shù)據(jù)庫(kù)不一樣而已,但是我們需要?jiǎng)?chuàng)建兩個(gè)不同的上下文實(shí)例,很顯然這不是最佳實(shí)踐方式。
那么我們?nèi)绾巫霾攀亲罴褜?shí)踐方式呢?接下來(lái)我們?cè)賮?lái)創(chuàng)建一個(gè)Demo3數(shù)據(jù)庫(kù),表結(jié)構(gòu)和Demo1、Demo2一致,如下:
接下來(lái)我們?cè)?NET Core Web應(yīng)用程序Demo1、Demo2上下文所在的類庫(kù)中創(chuàng)建如下擴(kuò)展方法(方便有同行需要學(xué)習(xí),給出Demo項(xiàng)目基本結(jié)構(gòu))。
我們暫且不去看為何這樣設(shè)置,我們只是添加上下文擴(kuò)展方法,更改連接為Demo3的數(shù)據(jù)庫(kù),然后接下來(lái)我們獲取博客列表時(shí),調(diào)用上述擴(kuò)展方法,請(qǐng)問:是否可以獲取到Demo3的數(shù)據(jù)或者說(shuō)是否會(huì)拋出異常呢?我們依然通過(guò)動(dòng)態(tài)圖來(lái)進(jìn)行演示,如下:
一直以來(lái)我們認(rèn)為利用 context.Database.GetDbConnection() 方法可以回到ADO.NET進(jìn)行查詢。
但是我們通過(guò)實(shí)際證明,我們可以設(shè)置其他數(shù)據(jù)庫(kù)連接從而達(dá)到讀寫分離最佳實(shí)踐方式,免去再實(shí)例化一個(gè)上下文。
所以對(duì)于上述我們配置的Demo1和Demo2上下文,我們大可只需要Demo1上下文即主數(shù)據(jù)庫(kù),對(duì)于從數(shù)據(jù)庫(kù)進(jìn)行查詢,我們只需在Demo1上下文的基礎(chǔ)上更該連接字符串即可,如下:
接下來(lái)問題來(lái)了,那么為何更改Demo1上下文連接字符串就能轉(zhuǎn)移到其他數(shù)據(jù)庫(kù)查詢呢?就是為了解決讀寫分離免去實(shí)例化上下文即Demo2的情況,但是內(nèi)部是如何實(shí)現(xiàn)的呢?
因?yàn)镋F Core內(nèi)部添加了方法實(shí)現(xiàn)IRelationalConnection接口,使得我們可以在已存在的上下文實(shí)例上重新設(shè)置連接字符串即更換數(shù)據(jù)庫(kù),但是其前提是必須保證當(dāng)前上下文連接已關(guān)閉。
也就是說(shuō)比如我們?cè)谕粋€(gè)事務(wù)中利用當(dāng)前上下文進(jìn)行更改操作,然后更改連接字符串進(jìn)行更改操作,最后提交事務(wù),因?yàn)樵诖耸聞?wù)內(nèi),當(dāng)前上下文連接還未關(guān)閉,所以再更改連接字符串后進(jìn)行數(shù)據(jù)庫(kù)更改操作,將必定會(huì)拋出異常。
花了兩天時(shí)間研究研究,本文比較詳細(xì)講解了對(duì)于讀寫分離后,如何進(jìn)行數(shù)據(jù)查詢和更改操作最佳實(shí)踐方式,不知道算不算最好的解決方案,若您有更好的方案,歡迎一起探討或者說(shuō)還有其他理解和疑問,也歡迎在留言中提出,下節(jié)我們講講ASP.NET Core MVC基礎(chǔ)系列。
總結(jié)
以上是生活随笔為你收集整理的EntityFramework Core进行读写分离最佳实践方式,了解一下?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: .Net Core小技巧 - Hoste
- 下一篇: Identity Server 4 预备