.NET开源分表框架:EFCore.Sharding
读写分离分库分表一直是数据库领域中的重难点,当数据规模达到单库极限的时候,就不得不考虑分表方案。EF Core作为.NET Core中最为主流的ORM,用起来十分方便快捷,但是官方并没有相应的Sharding支持,鄙人不才,经过一番摸索之后终于完成这个框架。
本框架旨在为EF Core提供Sharding(即读写分离分库分表)支持,不仅提供了一套强大的普通数据操作接口,并且降低了分表难度,支持按时间自动分表扩容,提供的操作接口简洁统一。
准备工作
配置工作
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是指添加抽象数据库,抽象数据库就是将多个分库看成同一个数据库来进行操作
AddPhysicDbGroup是指添加物理数据库组,在同一组物理数据库中,它们数据库类型相同,拥有的表相同,每个数据库拥有的数据是一致的(之间通过主主复制或主从复制进行数据同步)
AddPhysicTable是指添加物理数据表,传入的Base_UnitTest是抽象数据表(即将Base_UnitTest拆分为Base_UnitTest_0~2)
Base_UnitTestShardingRule是采用的分表规则,上述代码中采用的是哈希取模的分表方式
开始使用
IShardingRepository _db = DbFactory.GetRepository().ToSharding();
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}};//添加单条数据_db.Insert(_newData);//添加多条数据_db.Insert(_insertList);//清空表_db.DeleteAll<Base_UnitTest>();//删除单条数据_db.Delete(_newData);//删除多条数据_db.Delete(_insertList);//删除指定数据_db.Delete<Base_UnitTest>(x => x.UserId == "Admin2");//更新单条数据_db.Update(_newData);//更新多条数据_db.Update(_insertList);//更新单条数据指定属性_db.UpdateAny(_newData, new List<string> { "UserName", "Age" });//更新多条数据指定属性_db.UpdateAny(_insertList, new List<string> { "UserName", "Age" });//更新指定条件数据_db.UpdateWhere<Base_UnitTest>(x => x.UserId == "Admin", x =>{x.UserId = "Admin2";});//GetList获取表的所有数据var list=_db.GetList<Base_UnitTest>();//GetIQPagination获取分页后的数据var list=_db.GetIShardingQueryable<Base_UnitTest>().GetPagination(pagination);//Maxvar max=_db.GetIShardingQueryable<Base_UnitTest>().Max(x => x.Age);//Minvar min=_db.GetIShardingQueryable<Base_UnitTest>().Min(x => x.Age);//Averagevar min=_db.GetIShardingQueryable<Base_UnitTest>().Average(x => x.Age);//Countvar 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);
按时间自动分表
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>/// 表都在同一个数据库中/// </summary>public static void OneGroup(){DateTime startTime = DateTime.Now.AddMinutes(-5);DateTime endTime = DateTime.MaxValue;//配置初始化ShardingConfig.Init(config =>{config.AddAbsDb(DatabaseType.SqlServer)//添加抽象数据库.AddPhysicDbGroup()//添加物理数据库组.AddPhysicDb(ReadWriteType.Read | ReadWriteType.Write, Config.ConString1)//添加物理数据库1.SetShardingRule(new Base_UnitTestShardingRule())//设置分表规则.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($"当前数据量:{count}");Thread.Sleep(50);}}/// <summary>/// 表分布在两个数据库测试/// </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)//添加抽象数据库.AddPhysicDbGroup(group1)//添加物理数据库组1.AddPhysicDbGroup(group2)//添加物理数据库组2.AddPhysicDb(ReadWriteType.Read | ReadWriteType.Write, Config.ConString1, group1)//添加物理数据库1.AddPhysicDb(ReadWriteType.Read | ReadWriteType.Write, Config.ConString2, group2)//添加物理数据库2.SetShardingRule(new Base_UnitTestShardingRule())//设置分表规则.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($"当前数据量:{count}");Thread.Sleep(50);}}));}Console.ReadLine();}static void Main(string[] args){OneGroup();Console.ReadLine();}}}
上面的代码实现了将Base_UnitTest表按照时间自动分表,每分钟创建一张表,实际使用中根据业务需求设置ExpandByDateMode参数,常用按天、按月分表。自动分表的效果如下所示,全程无需人工干预,系统会自动定时创建分表,十分简单好用。
性能测试
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万数据,然后将这三张表的数据导入Base_UnitTest中(即Base_UnitTest表的数据与Base_UnitTest_0-2三张表总合数据一致)。
分表与不分表测试结果如下:
其他简单操作(非Sharding)
详细使用方式参考:https://github.com/Coldairarrow/EFCore.Sharding/blob/master/examples/Demo.DI/Program.cs
这个简单实用强大的框架希望能够帮助到大家,力求为.NET生态贡献一份力,大家一起壮大.NET生态。
欢迎使用本框架,若觉得不错,请在GitHub上给本项目比心!
出处:https://www.cnblogs.com/coldairarrow/p/12733886.html
编辑:Edison Zhou
往期精彩回顾
👇点击阅读原文,进入项目主页
