.NET ORM 分表分庫【到底】怎么做?

一、理論知識
分表 - 從表面意思上看呢,就是把一張表分成N多個小表,每一個小表都是完正的一張表。分表后數(shù)據(jù)都是存放在分表里,總表只是一個外殼,存取數(shù)據(jù)發(fā)生在一個一個的分表里面。分表后單表的并發(fā)能力提高了,磁盤I/O性能也提高了。并發(fā)能力為什么提高了呢,因為查尋一次所花的時間變短了,如果出現(xiàn)高并發(fā)的話,總表可以根據(jù)不同 的查詢,將并發(fā)壓力分到不同的小表里面。
分庫 - 把原本存儲于一個庫的數(shù)據(jù)分塊存儲到多個庫上,把原本存儲于一個表的數(shù)據(jù)分塊存儲到多個表上。數(shù)據(jù)庫中的數(shù)據(jù)量不一定是可控的,在未進(jìn)行分表分庫的情況下,隨著時間和業(yè)務(wù)的發(fā)展,庫中的表會越來越多,表中的數(shù)據(jù)量也會越來越大,相應(yīng)地,數(shù)據(jù)操作,增刪改查的開銷也會越來越大;另外,一臺服務(wù)器的資源(CPU、磁盤、內(nèi)存、IO等)是有限的,最終數(shù)據(jù)庫所能承載的數(shù)據(jù)量、數(shù)據(jù)處理能力都將遭遇瓶頸
二、情懷滿滿
分表、分庫在 .NET 下可謂是老大難題,簡單點可以使用類似 mycat 中間件,但是就 .NET 平臺的自身生態(tài),很缺乏類似 sharding-jdbc 這樣強大的輪子。
本人就自身有限的技術(shù)水平和經(jīng)驗,對分表、分庫進(jìn)行分析,實現(xiàn)出自成一套的使用方法,雖然不極 sharding-jdbc 強大,但是還算比較通用、簡單。但愿有朝一日出現(xiàn)一批真正 .NET 大神,造出偉大的開源項目,實現(xiàn)你我心中的抱負(fù)。
這套分表、分庫方法是建立在 .NET ORM FreeSql 之上做的,內(nèi)容可能比較抽象,敬請諒解!后續(xù)會詳解各種租戶設(shè)計方案,除了按字段區(qū)分租戶,還包括分庫、分表的方案,敬請關(guān)注!
三、入戲準(zhǔn)備
FreeSql 是 .Net ORM,能支持 .NetFramework4.0+、.NetCore、Xamarin、XAUI、Blazor、以及還有說不出來的運行平臺,因為代碼綠色無依賴,支持新平臺非常簡單。目前單元測試數(shù)量:5000+,Nuget下載數(shù)量:180K+,源碼幾乎每天都有提交。值得高興的是 FreeSql 加入了 ncc 開源社區(qū):https://github.com/dotnetcore/FreeSql,加入組織之后社區(qū)責(zé)任感更大,需要更努力做好品質(zhì),為開源社區(qū)出一份力。
QQ群:4336577(已滿)、8578575(在線)、52508226(在線)
為什么要重復(fù)造輪子?
FreeSql 主要優(yōu)勢在于易用性上,基本是開箱即用,在不同數(shù)據(jù)庫之間切換兼容性比較好。作者花了大量的時間精力在這個項目,肯請您花半小時了解下項目,謝謝。功能特性如下:
支持 CodeFirst 對比結(jié)構(gòu)變化遷移; 支持 DbFirst 從數(shù)據(jù)庫導(dǎo)入實體類; 支持 豐富的表達(dá)式函數(shù),自定義解析; 支持 批量添加、批量更新、BulkCopy; 支持 導(dǎo)航屬性,貪婪加載、延時加載、級聯(lián)保存; 支持 讀寫分離、分表分庫,租戶設(shè)計; 支持 MySql/SqlServer/PostgreSQL/Oracle/Sqlite/達(dá)夢/神通/人大金倉/MsAccess;FreeSql 使用非常簡單,【單機(jī)數(shù)據(jù)庫】只需要定義一個 IFreeSql 對象即可:
static IFreeSql fsql = new FreeSql.FreeSqlBuilder().UseConnectionString(FreeSql.DataType.MySql, connectionString).UseAutoSyncStructure(true) //自動同步實體結(jié)構(gòu)到數(shù)據(jù)庫.Build(); //請務(wù)必定義成 Singleton 單例模式
四、分表
既然是分表,那就大膽認(rèn)為他是操作【單機(jī)數(shù)據(jù)庫】,只需要對實體類進(jìn)行動態(tài)映射表名即可實現(xiàn),F(xiàn)reeSql 原生用法、FreeSql.Repository 倉儲用法 都提供了 AsTable 方法對分表進(jìn)行 CRUD 操作,例如:
var repo = fsql.GetRepository(); repo.AsTable(oldname => $"{oldname}_201903");//對 Log_201903 表 CRUDrepo.Insert(new Log { ... });repo.Update(...);repo.Delete(...);repo.Select...;
AsTable 動態(tài)設(shè)置實體映射的表名,達(dá)到對分表的操作目的。除了 CRUD 操作,還提供了創(chuàng)建分表的功能:
如果開啟了自動同步結(jié)構(gòu)功能 UseAutoSyncStructure(true),則 AsTable 會自動創(chuàng)建對應(yīng)分表; 可以使用 fsql.CodeFirst.SyncStructure(typeof(實體類), "分表名") 進(jìn)行手工建表;
多數(shù)情況,我們都建議提前創(chuàng)建好分表,如果按月分表,手工創(chuàng)建一年的分表。目前這種算是比較簡單入門的方案,遠(yuǎn)不及 mycat、sharding-jdbc 那么智能,比如:
不能利用分表字段自動進(jìn)行分表映射; 不能在查詢時根據(jù) where 條件自動映射分表,甚至跨多個分表的聯(lián)合查詢;
五、分庫(單機(jī))
分庫,但是在同一個數(shù)據(jù)庫服務(wù)器實例下。這種情況也可以使用 AsTable 方式進(jìn)行操作,如下:
var repo = fsql.GetRepository(); repo.AsTable(oldname => $"{201903}.dbo.{oldname}");//對 [201903].dbo.Log CRUD
分庫之后,老大難題是事務(wù),如果使用 SqlServer 可以利用 TransactionScope 做簡單的跨庫事務(wù),如下:
var repoLog = fsql.GetRepository(); var repoComment = fsql.GetRepository(); repoLog.AsTable(oldname => $"{201903}.dbo.{oldname}");repoComment.AsTable(oldname => $"{201903}.dbo.{oldname}");using (TransactionScope ts = new TransactionScope()){repoComment.Insert(new Comment { ... });repoLog.Insert(new Log { ... });ts.Complete();}
六、分庫(跨服務(wù)器)
前面提到:【單機(jī)數(shù)據(jù)庫】只需要定義一個 IFreeSql 對象即可。那分庫是不是要定義很多個 IFreeSql 對象?答案是的。
一般思路可以定義 static ConcurrentDictionary
更好的辦法可以使用 IdleBus 空閑對象管理容器,有效組織對象重復(fù)利用,自動創(chuàng)建、銷毀,解決【實例】過多且長時間占用的問題。有時候想做一個單例對象重復(fù)使用提升性能,但是定義多了,有的又可能一直空閑著占用資源。專門解決:又想重復(fù)利用,又想少占資源的場景。https://github.com/2881099/IdleBus
dotnet add package IdleBus
static IdleBusib = new IdleBus (TimeSpan.FromMinutes(10)); ib.Register("db1", () => new FreeSqlBuilder().UseConnectionString(DataType.MySql, "str1").Build());ib.Register("db2", () => new FreeSqlBuilder().UseConnectionString(DataType.MySql, "str2").Build());ib.Register("db3", () => new FreeSqlBuilder().UseConnectionString(DataType.SqlServer, "str3").Build());//...注冊很多個ib.Get("db1").Select().Limit(10).ToList();
IdleBus 也是【單例】設(shè)計!主要的兩個方法,注冊,獲取。idlebus 注冊不是創(chuàng)建 IFreeSql,首次 Get 時才創(chuàng)建,后面會一直用已經(jīng)創(chuàng)建的。還有一個超時機(jī)制,如果 10 分鐘該 IFreeSql 未使用會被 Dispose,然后下一次又會創(chuàng)建新的 IFreeSql,如此反復(fù)。從而解決了 10000 個 IFreeSql 長駐內(nèi)存的問題。
還利用 AsyncLocal 特性擴(kuò)展使用起來更加方便:
public static class IdleBusExtesions{static AsyncLocalasyncDb = new AsyncLocal (); public static IdleBusChangeDatabase(this IdleBus ib, string db) {asyncDb.Value = db;return ib;}public static IFreeSql Get(this IdleBusib) => ib.Get(asyncDb.Value ?? "db1"); public static IBaseRepositoryGetRepository (this IdleBus ib) where T : class => ib.Get().GetRepository(); }
使用 ChangeDatabase 切換 db; 使用 Get() 獲取當(dāng)前 IFreeSql,省略每次都傳遞 db 參數(shù); 使用 GetRepository 獲取當(dāng)前 IFreeSql 對應(yīng)的倉儲類;
注意:使用 IdleBus 需要弱化 IFreeSql 的存在,每次都使用 ib.Get 獲取 IFreeSql 對象;
IdleBusib = ...; //單例注入 var fsql = ib.Get(); //獲取當(dāng)前租戶對應(yīng)的 IFreeSqlvar fsql00102 = ib.ChangeDatabase("db2").Get(); //切換租戶,后面的操作都是針對 db2var songRepository = ib.GetRepository(); var detailRepository = ib.GetRepository();
目前這種算是比較簡單入門的方案,遠(yuǎn)不及 mycat、sharding-jdbc 那么智能,比如:沒有實現(xiàn)跨庫事務(wù)。
七、寫在最后
.NET 生態(tài)還處于較弱的狀態(tài),呼吁大家支持、踴躍參與開源項目,為下一個 .NET 開源社區(qū)五年計劃做貢獻(xiàn)。
希望正在使用的、善良的您能動一動小手指,把文章轉(zhuǎn)發(fā)一下,讓更多人知道 .NET 有這樣一個好用的 ORM 存在。謝謝了!!
FreeSql 開源協(xié)議 MIT https://github.com/dotnetcore/FreeSql,可以商用,文檔齊全。QQ群:4336577(已滿)、8578575(在線)、52508226(在線)
如果你有好的 ORM 實現(xiàn)想法,歡迎給作者留言討論,謝謝觀看!
【推薦】.NET Core開發(fā)實戰(zhàn)視頻課程?★★★
.NET Core實戰(zhàn)項目之CMS 第一章 入門篇-開篇及總體規(guī)劃
【.NET Core微服務(wù)實戰(zhàn)-統(tǒng)一身份認(rèn)證】開篇及目錄索引
Redis基本使用及百億數(shù)據(jù)量中的使用技巧分享(附視頻地址及觀看指南)
.NET Core中的一個接口多種實現(xiàn)的依賴注入與動態(tài)選擇看這篇就夠了
用abp vNext快速開發(fā)Quartz.NET定時任務(wù)管理界面
在ASP.NET Core中創(chuàng)建基于Quartz.NET托管服務(wù)輕松實現(xiàn)作業(yè)調(diào)度
現(xiàn)身說法:實際業(yè)務(wù)出發(fā)分析百億數(shù)據(jù)量下的多表查詢優(yōu)化
給我好看
您看此文用
?
?
·
?
秒,轉(zhuǎn)發(fā)只需1秒呦~

好看你就
點點
我

