天天看點

efcore分表分庫原了解析

<code>ShardingCore</code> 易用、簡單、高性能、普适性,是一款擴充針對efcore生态下的分表分庫的擴充解決方案,支援efcore2+的所有版本,支援efcore2+的所有資料庫、支援自定義路由、動态路由、高性能分頁、讀寫分離的一款元件,如果你喜歡這元件或者這個元件對你有幫助請點選下發star讓更多的.neter可以看到使用

Gitee Star 助力dotnet 生态 Github Star

經過了3個星期再次發一篇部落格來介紹本架構的實作原理通過本篇文章可以有助于您閱讀源碼和提出寶貴意見。之前通過兩篇文章簡單的介紹了sharding-core的核心聚合原理(ShardingCore 如何呈現“完美”分表)和高性能分頁原理實作(ShardingCore是如何針對分表下的分頁進行優化的),這兩篇文章主要是針對分表分庫下資料擷取的一個解決方案的思路并不涉及到太多efcore(.net)的知識。

efcore分表分庫原了解析

通過關系圖我們可以看到目前一個shardingdbcontext下主要是以entity作為媒介通過兩個虛拟表和虛拟資料源為橋梁來實作一對多的關系映射

首先先說下經過了3個星期目前本架構已經具有了3個星期前不具備的一些功能,主要是有以下幾個功能上的改進和添加

之前的架構僅支援分表,思路是先将分表做到相對完成度比較高後在實作分庫,畢竟分表對于大部分使用者而言使用場景更高,目前已經實作針對資料對象實作了分庫的實作,當然您還是可以在分庫的基礎上在實作分表,這兩者是不沖突的

相信很多使用efcore的使用者其實是更加喜歡脫離資料庫開發,在開發的時候不進行資料庫層面的操作而隻專注于代碼的業務編寫來保證高效性,配合efcore的fluent api 可以做到很完美的開發時候不關注資料庫,效率拉滿 Migrations

efcore的好用功能之一(自動追蹤)開啟後可以幫助程式實作更多的功能,雖然之前也是支援的但是就是用體驗而言之前的需要手動attach而目前支援了自動化,當然也不可能和efcore原生的100%完美,當然架構預設不開啟自動追蹤

說人話就是本次查詢路由坐落到10張表,之前的做法是開啟10個線程并行查詢10次後擷取到對應的疊代器,目前添加了核心查詢線程數控制,如果您設定了5,本次查詢路由到10張表,會議開始開啟5個線程,後續每完成一個開啟一個新新線程,并且支援逾時時間,可以保證在一定時間内執行完成,完不成就逾時,防止查詢坐落的表過多而一次性大量開啟線程進而導緻程式消耗過多資源

架構目前支援全局定義和局部定義是否啟用讀寫分離,如果您開啟了讀寫分離那麼資料庫和資料庫之間的資料同步延遲會是一個很嚴重的問題他會讓你沒辦法很好的查詢到剛修改的資料,而sharding-core為這個場景提供了手動切換是否使用writeonly字元串;用來保證消除讀寫分離時帶來的延遲,而造成資料處理上的異常。而且程式也提供了讀寫分離政策除了随機和輪詢外額外有一個配置可以配置讀寫分離真正執行是依據dbcontext還是每次都是最新的,每次都是最新的會有一個問題,你明明分頁count出來是10條可能查詢隻傳回了9條或者其他資料,是以再次基礎上進行了設定是否按dbcontext就是說同一個dbcontext是一樣的連結,dbcontext預設是scope就是說一次請求下面是一樣的當然也可以設定成每次都是最新的具體自行考慮根據業務

以上一些功能的添加和優化是之前sharding-core版本所不具備的,其他功能也在不斷的完善中。

接下來我将來講解下sharding-core的實作原理如何讓efcore實作sharding功能,并且完美的無感覺使用dbcontext。

efcore分表分庫原了解析

在sharding-core中核心api接口依然是通過dbcontext的繼承來實作的,首先是攔截sql,總的有兩條路可以走1.通過efcore提供的攔截器攔截sql配合antlr4實作對sql語句的分析和從新分裂出對應的語句來進行查詢最後通過多個datareader進行流式聚合。2.通過攔截iqueryable的lambda表達式來分裂成多個ienumerator進行聚合,在這裡我選擇了後者因為相比表達式的解析字元串的解析更加吃力而且本人也不是很熟悉antlr4是以選擇了後者。那麼如何進行攔截的,這個熟悉linq的同學肯定都知道一個iqueryable都會有一個對應的provider這兩個是一對的,又得益于efcore的開放型設計通過替換兩個核心接口來實作<code>IDbSetSource</code>和<code>IQueryCompiler</code>,下面就簡單說下這兩個接口在efcore中的作用

用于針對efcore的<code>dbcontext.set&lt;entity&gt;()</code>和<code>dbset&lt;entity&gt;()</code>進行攔截和api重構具體是現代嗎ShardingDbSetSource

efcore核心查詢編譯,用于對表達式進行編譯後緩存起來,所有的查詢都會通過<code>IQueryCompiler</code>核心接口,那麼通過自己實作這兩個接口接管對應的表達式後對表達式進行分析就可以擷取到對應的where子句,在通過将表達式進行路由後并行請求流式聚合傳回對應的<code>IEnumerator</code>或者<code>IAsyncEnumerator</code>就可以實作無感覺使用sharding-core,感覺和使用efcore一毛一樣。具體實作代碼ShardingQueryCompiler

用過efcore的都應該知道目前efcore的機制就是一個對象一張表,在這個機制下面如果你想實作上圖的功能隻能建立多個dbcontext然後讓對應的dbcontext的對象映射到對應的表裡面而不是固定的Entitiy對應table,那麼如何讓對應的對象Entity對應table1和table2和table3呢?

說人話就是我可以再這邊通過modelBuilder擷取我自己想要的對象但是如果我把Entity映射到了table1那麼這個dbcontext就會被緩存起來entity-table1這個關系也會被緩存起來沒辦法改變了,那麼是否有辦法可以解決這個機制呢有兩個efcore的接口可以幫助我們實作這個功能,這個在部落格園很多大神都已經實作過了具體是<code>IModelCacheKeyFactory</code>和 <code>IModelCustomizer</code>

用于将efcore的模型緩存進行判斷是否和之前的模型緩存一緻具體實作ShardingModelCacheKeyFactory

這個接口是efcore開放出來在模型緩存結構定義完成後初始化緩存前可以使用的接口,就是說我們并不需要在OnModelCreating方法中使用或者說不需要再次地方進行修改可以在<code>IModelCustomizer</code>接口内部實作,具體代碼ShardingModelCustomizer

稍作解析進入後會先判斷dbcontext真正執行的那個是否是需要分表的并且判斷本次查詢涉及到的表示一張還是多張,對此對象在資料庫裡的映射關系改成分表

到此為止efcore的查詢架構已經算是非常清晰了

通過替換模型緩存接口和查詢編譯接口來實作查詢編譯時攔截sql和模型重建

通過類似擴充卡模式來實作對外dbcontext其實内部有多個dbcontext在進行真正的工作

上述幾步讓sharding-core在使用上和efcore一樣除了配置方面,後續将會出更多的efcore的分表分庫實踐文章和繼續開發完成其他orm的支援,當然這個改動将會非常大也希望各位.neter有喜歡的或者希望了解源碼的或者想參與完善的多多支援

下一篇實作如何自定義路由,自定義路由的原理 where left

部落格

QQ群:771630778

個人QQ:326308290(歡迎技術支援提供您寶貴的意見)

個人郵箱:[email protected]

繼續閱讀