EFCore.Sharding(EFCore开源分表框架)
簡介
本框架旨在為EF Core提供Sharding(即讀寫分離分庫分表)支持,不僅提供了一套強大的普通數(shù)據(jù)操作接口,并且降低了分表難度,支持按時間自動分表擴容,提供的操作接口簡潔統(tǒng)一.
源碼地址:EFCore.SHarding
引言
讀寫分離分庫分表一直是數(shù)據(jù)庫領域中的重難點,當數(shù)據(jù)規(guī)模達到單庫極限的時候,就不得不考慮分表方案。EF Core作為.NET Core中最為主流的ORM,用起來十分方便快捷,但是官方并沒有相應的Sharding支持,鄙人不才,經(jīng)過一番摸索之后終于完成這個框架.
開始
準備
首先根據(jù)需要安裝對應的Nuget包
| EFCore.Sharding | 必裝包,3.x版本對應EF Core3.x,2.x版本對應EF Core2.x |
| EFCore.Sharding.MySql | MySql支持 |
| EFCore.Sharding.PostgreSql | PostgreSql支持 |
| EFCore.Sharding.SQLite | SQLite支持 |
| EFCore.Sharding.SqlServer | SqlServer支持 |
| EFCore.Sharding.Oracle | Oracle支持(暫不支持3.x) |
配置
class Base_UnitTestShardingRule : ModShardingRule<Base_UnitTest> {protected override string KeyField => "Id";protected override int Mod => 3; }ShardingConfig.Init(config => {config.AddAbsDb(DatabaseType.SQLite).AddPhysicDb(ReadWriteType.Read | ReadWriteType.Write, "DataSource=db.db").AddPhysicDbGroup().AddPhysicTable<Base_UnitTest>("Base_UnitTest_0").AddPhysicTable<Base_UnitTest>("Base_UnitTest_1").AddPhysicTable<Base_UnitTest>("Base_UnitTest_2").SetShardingRule(new Base_UnitTestShardingRule()); });上述代碼中完成了Sharding配置
AddAbsDb是指添加抽象數(shù)據(jù)庫,抽象數(shù)據(jù)庫就是將多個分庫看成同一個數(shù)據(jù)庫來進行操作
AddPhysicDbGroup是指添加物理數(shù)據(jù)庫組,在同一組物理數(shù)據(jù)庫中,它們數(shù)據(jù)庫類型相同,擁有的表相同,每個數(shù)據(jù)庫擁有的數(shù)據(jù)是一致的(之間通過主主復制或主從復制進行數(shù)據(jù)同步)
AddPhysicTable是指添加物理數(shù)據(jù)表,傳入的Base_UnitTest是抽象數(shù)據(jù)表(即將Base_UnitTest拆分為Base_UnitTest_0~2)
Base_UnitTestShardingRule是采用的分表規(guī)則,上述代碼中采用的是哈希取模的分表方式
使用
配置完成,下面開始使用,使用方式非常簡單,與平常使用基本一致
首先獲取分片倉儲接口IShardingRepository
然后即可進行數(shù)據(jù)操作:
Base_UnitTest _newData = new Base_UnitTest {Id = Guid.NewGuid().ToString(),UserId = "Admin",UserName = "超級管理員",Age = 22 }; List<Base_UnitTest> _insertList = new List<Base_UnitTest> {new Base_UnitTest{Id = Guid.NewGuid().ToString(),UserId = "Admin1",UserName = "超級管理員1",Age = 22},new Base_UnitTest{Id = Guid.NewGuid().ToString(),UserId = "Admin2",UserName = "超級管理員2",Age = 22} }; //添加單條數(shù)據(jù) _db.Insert(_newData); //添加多條數(shù)據(jù) _db.Insert(_insertList); //清空表 _db.DeleteAll<Base_UnitTest>(); //刪除單條數(shù)據(jù) _db.Delete(_newData); //刪除多條數(shù)據(jù) _db.Delete(_insertList); //刪除指定數(shù)據(jù) _db.Delete<Base_UnitTest>(x => x.UserId == "Admin2"); //更新單條數(shù)據(jù) _db.Update(_newData); //更新多條數(shù)據(jù) _db.Update(_insertList); //更新單條數(shù)據(jù)指定屬性 _db.UpdateAny(_newData, new List<string> { "UserName", "Age" }); //更新多條數(shù)據(jù)指定屬性 _db.UpdateAny(_insertList, new List<string> { "UserName", "Age" }); //更新指定條件數(shù)據(jù) _db.UpdateWhere<Base_UnitTest>(x => x.UserId == "Admin", x => {x.UserId = "Admin2"; }); //GetList獲取表的所有數(shù)據(jù) var list=_db.GetList<Base_UnitTest>(); //GetIQPagination獲取分頁后的數(shù)據(jù) var list=_db.GetIShardingQueryable<Base_UnitTest>().GetPagination(pagination); //Max var max=_db.GetIShardingQueryable<Base_UnitTest>().Max(x => x.Age); //Min var min=_db.GetIShardingQueryable<Base_UnitTest>().Min(x => x.Age); //Average var min=_db.GetIShardingQueryable<Base_UnitTest>().Average(x => x.Age); //Count var min=_db.GetIShardingQueryable<Base_UnitTest>().Count(); //事務,使用方式與普通事務一致 bool succcess = _db.RunTransaction(() => {_db.Insert(_newData);var newData2 = _newData.DeepClone();_db.Insert(newData2); }).Success; Assert.AreEqual(succcess, false);上述操作中表面上是操作Base_UnitTest表,實際上卻在按照一定規(guī)則使用Base_UnitTest_0~2三張表,使分片對業(yè)務操作透明,極大提高開發(fā)效率
具體使用方式請參考單元測試源碼:連接
按時間自動分表
上面的哈希取模的方式雖然簡單,但是卻十分不實用,因為當3張分表到達瓶頸時,將會面臨擴容的問題,這種方式擴容需要進行大量的數(shù)據(jù)遷移,這無疑是十分麻煩的。因此需要一種方式能夠系統(tǒng)自動建表擴容,并且無需人工干預,這就是按時間自動分表.
using Demo.Common; using EFCore.Sharding; using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks;namespace Demo.AutoExpandByDate {class Base_UnitTestShardingRule : AbsShardingRule<Base_UnitTest>{public override DateTime BuildDate(Base_UnitTest obj){return obj.CreateTime;}}class Program{/// <summary>/// 表都在同一個數(shù)據(jù)庫中/// </summary>public static void OneGroup(){DateTime startTime = DateTime.Now.AddMinutes(-5);DateTime endTime = DateTime.MaxValue;//配置初始化ShardingConfig.Init(config =>{config.AddAbsDb(DatabaseType.SqlServer)//添加抽象數(shù)據(jù)庫.AddPhysicDbGroup()//添加物理數(shù)據(jù)庫組.AddPhysicDb(ReadWriteType.Read | ReadWriteType.Write, Config.ConString1)//添加物理數(shù)據(jù)庫1.SetShardingRule(new Base_UnitTestShardingRule())//設置分表規(guī)則.AutoExpandByDate<Base_UnitTest>(//設置為按時間自動分表ExpandByDateMode.PerMinute,(startTime, endTime, ShardingConfig.DefaultDbGourpName));});var db = DbFactory.GetShardingRepository();while (true){db.Insert(new Base_UnitTest{Id = Guid.NewGuid().ToString(),Age = 1,UserName = Guid.NewGuid().ToString(),CreateTime = DateTime.Now});var count = db.GetIShardingQueryable<Base_UnitTest>().Count();Console.WriteLine($"當前數(shù)據(jù)量:{count}");Thread.Sleep(50);}}/// <summary>/// 表分布在兩個數(shù)據(jù)庫測試/// </summary>public static void TwoGroup(){DateTime startTime1 = DateTime.Now.AddMinutes(-5);DateTime endTime1 = DateTime.Now.AddMinutes(5);DateTime startTime2 = endTime1;DateTime endTime2 = DateTime.MaxValue;string group1 = "group1";string group2 = "group2";//配置初始化ShardingConfig.Init(config =>{config.AddAbsDb(DatabaseType.SqlServer)//添加抽象數(shù)據(jù)庫.AddPhysicDbGroup(group1)//添加物理數(shù)據(jù)庫組1.AddPhysicDbGroup(group2)//添加物理數(shù)據(jù)庫組2.AddPhysicDb(ReadWriteType.Read | ReadWriteType.Write, Config.ConString1, group1)//添加物理數(shù)據(jù)庫1.AddPhysicDb(ReadWriteType.Read | ReadWriteType.Write, Config.ConString2, group2)//添加物理數(shù)據(jù)庫2.SetShardingRule(new Base_UnitTestShardingRule())//設置分表規(guī)則.AutoExpandByDate<Base_UnitTest>(//設置為按時間自動分表ExpandByDateMode.PerMinute,(startTime1, endTime1, group1),(startTime2, endTime2, group2));});List<Task> tasks = new List<Task>();for (int i = 0; i < 4; i++){tasks.Add(Task.Run(() =>{var db = DbFactory.GetShardingRepository();while (true){db.Insert(new Base_UnitTest{Id = Guid.NewGuid().ToString(),Age = 1,UserName = Guid.NewGuid().ToString(),CreateTime = DateTime.Now});var count = db.GetIShardingQueryable<Base_UnitTest>().Count();Console.WriteLine($"當前數(shù)據(jù)量:{count}");Thread.Sleep(50);}}));}Console.ReadLine();}static void Main(string[] args){OneGroup();Console.ReadLine();}} }上面Demo都在源碼中
上面的代碼實現(xiàn)了將Base_UnitTest表按照時間自動分表,每分鐘創(chuàng)建一張表,實際使用中根據(jù)業(yè)務需求設置ExpandByDateMode參數(shù),常用按天、按月分表
自動分表效果
全程無需人工干預,系統(tǒng)會自動定時創(chuàng)建分表,十分簡單好用
性能測試
using Demo.Common; using EFCore.Sharding; using System; using System.Diagnostics; using System.Linq;namespace Demo.Performance {class Base_UnitTestShardingRule : ModShardingRule<Base_UnitTest>{protected override string KeyField => "Id";protected override int Mod => 3;}class Program{static void Main(string[] args){ShardingConfig.Init(config =>{config.AddAbsDb(DatabaseType.SqlServer).AddPhysicDb(ReadWriteType.Read | ReadWriteType.Write, Config.ConString1).AddPhysicDbGroup().AddPhysicTable<Base_UnitTest>("Base_UnitTest_0").AddPhysicTable<Base_UnitTest>("Base_UnitTest_1").AddPhysicTable<Base_UnitTest>("Base_UnitTest_2").SetShardingRule(new Base_UnitTestShardingRule());});var db = DbFactory.GetRepository(Config.ConString1, DatabaseType.SqlServer);Stopwatch watch = new Stopwatch();var q = db.GetIQueryable<Base_UnitTest>().Where(x => x.UserName.Contains("00001C22-8DD2-4D47-B500-407554B099AB")).OrderByDescending(x => x.Id).Skip(0).Take(30);q.ToList();q.ToSharding().ToList();watch.Restart();var list1 = q.ToList();watch.Stop();Console.WriteLine($"未分表耗時:{watch.ElapsedMilliseconds}ms");watch.Restart();var list2 = q.ToSharding().ToList();watch.Stop();Console.WriteLine($"分表后耗時:{watch.ElapsedMilliseconds}ms");Console.WriteLine("完成");}} }分表Base_UnitTest_0-2各有100萬數(shù)據(jù),然后將這三張表的數(shù)據(jù)導入Base_UnitTest中(即Base_UnitTest表的數(shù)據(jù)與Base_UnitTest_0-2三張表總合數(shù)據(jù)一致)
分表與不分表測試結果如下
這里僅僅分了3張表,其效果立桿見影,若分表幾十張,那效果想想就很棒
其它簡單操作(非Sharing)
框架不僅支持Sharing,而且封裝了常用數(shù)據(jù)庫操作,使用比較簡單
詳細使用方式參考?鏈接
總結
這個簡單實用強大的框架希望能夠幫助到大家,力求為.NET生態(tài)貢獻一份力,大家一起壯大.NET生態(tài)
歡迎使用本框架,若覺得不錯,請比心
Github歡迎星星:https://github.com/Coldairarrow
博客園歡迎點贊:https://www.cnblogs.com/coldairarrow/
QQ群3:940069478
個人QQ:862520575(歡迎技術支持及商務合作,提供.NET Core + Linux + Nginx+ jenkins + git整套持續(xù)集成快速開發(fā)平臺)
總結
以上是生活随笔為你收集整理的EFCore.Sharding(EFCore开源分表框架)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [一起读源码]走进C#并发队列Concu
- 下一篇: k8s中流量分离以及资源隔离实战