理论知识
分表 – 从外表意思上看呢,就是把一张表分成 N 多个小表,每一个小表都是完正的一张表。分表后数据都是寄存在分表里,总表只是一个外壳,存取数据产生在一个一个的分表外面。分表后单表的并发能力进步了,磁盘 I / O 性能也进步了。并发能力为什么进步了呢,因为查寻一次所花的工夫变短了,如果呈现高并发的话,总表能够依据不同 的查问,将并发压力分到不同的小表外面。
分库 – 把本来存储于一个库的数据分块存储到多个库上,把本来存储于一个表的数据分块存储到多个表上。数据库中的数据量不肯定是可控的,在未进行分表分库的状况下,随着工夫和业务的倒退,库中的表会越来越多,表中的数据量也会越来越大,相应地,数据操作,增删改查的开销也会越来越大;另外,一台服务器的资源(CPU、磁盘、内存、IO 等)是无限的,最终数据库所能承载的数据量、数据处理能力都将遭逢瓶颈。
情怀满满
分表、分库在 .NET 下堪称是老大难题,简略点能够应用相似 mycat 中间件,然而就 .NET 平台的本身生态,很不足相似 sharding-jdbc 这样弱小的轮子。
自己就本身无限的技术水平和教训,对分表、分库进行剖析,实现出自成一套的应用办法,尽管不极 sharding-jdbc 弱小,然而还算比拟通用、简略。但愿有朝一日呈现一批真正 .NET 大神,造出平凡的开源我的项目,实现你我心中的抱负。
这套分表、分库办法是建设在 .NET ORM FreeSql 之上做的,内容可能比拟形象,敬请体谅!后续会详解各种租户设计方案,除了按字段辨别租户,还包含分库、分表的计划,敬请关注!
入戏筹备
FreeSql 是 .Net ORM,能反对 .NetFramework4.0+、.NetCore、Xamarin、XAUI、Blazor、以及还有说不出来的运行平台,因为代码绿色无依赖,反对新平台非常简单。目前单元测试数量:5000+,Nuget 下载数量:180K+,源码简直每天都有提交。值得快乐的是 FreeSql 退出了 ncc 开源社区:https://github.com/dotnetcore/FreeSql,退出组织之后社区责任感更大,须要更致力做好品质,为开源社区出一份力。
QQ 群:4336577(已满)、8578575(在线)、52508226(在线)
为什么要反复造轮子?
FreeSql 次要劣势在于易用性上,根本是开箱即用,在不同数据库之间切换兼容性比拟好。作者花了大量的工夫精力在这个我的项目,肯请您花半小时理解下我的项目,谢谢。性能个性如下:
- 反对 CodeFirst 比照构造变动迁徙;
- 反对 DbFirst 从数据库导入实体类;
- 反对 丰盛的表达式函数,自定义解析;
- 反对 批量增加、批量更新、BulkCopy;
- 反对 导航属性,贪心加载、延时加载、级联保留;
- 反对 读写拆散、分表分库,租户设计;
- 反对 MySql/SqlServer/PostgreSQL/Oracle/Sqlite/ 达梦 / 神通 / 人大金仓 /MsAccess;
FreeSql 应用非常简单,【单机数据库】只须要定义一个 IFreeSql 对象即可:
static IFreeSql fsql = new FreeSql.FreeSqlBuilder()
.UseConnectionString(FreeSql.DataType.MySql, connectionString)
.UseAutoSyncStructure(true) // 主动同步实体构造到数据库
.Build(); // 请务必定义成 Singleton 单例模式
分表
既然是分表,那就大胆认为他是操作【单机数据库】,只须要对实体类进行动静映射表名即可实现,FreeSql 原生用法、FreeSql.Repository 仓储用法 都提供了 AsTable 办法对分表进行 CRUD 操作,例如:
var repo = fsql.GetRepository<Log>();
repo.AsTable(oldname => $"{oldname}_201903");
// 对 Log_201903 表 CRUD
repo.Insert(new Log { ...});
repo.Update(...);
repo.Delete(...);
repo.Select...;
AsTable 动静设置实体映射的表名,达到对分表的操作目标。除了 CRUD 操作,还提供了创立分表的性能:
- 如果开启了主动同步构造性能 UseAutoSyncStructure(true),则 AsTable 会主动创立对应分表;
- 能够应用 fsql.CodeFirst.SyncStructure(typeof( 实体类), “ 分表名 ”) 进行手工建表;
少数状况,咱们都倡议提前创立好分表,如果按月分表,手工创立一年的分表。
目前这种算是比较简单入门的计划,远不迭 mycat、sharding-jdbc 那么智能,比方:
- 不能利用分表字段主动进行分表映射;
- 不能在查问时依据 where 条件主动映射分表,甚至跨多个分表的联结查问;
分库(单机)
分库,然而在同一个数据库服务器实例下。这种状况也能够应用 AsTable 形式进行操作,如下:
var repo = fsql.GetRepository<Log>();
repo.AsTable(oldname => $"{201903}.dbo.{oldname}");
// 对 [201903].dbo.Log CRUD
分库之后,老大难题是事务,如果应用 SqlServer 能够利用 TransactionScope 做简略的跨库事务,如下:
var repoLog = fsql.GetRepository<Log>();
var repoComment = fsql.GetRepository<Comment>();
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();}
分库(跨服务器)
后面提到:【单机数据库】只须要定义一个 IFreeSql 对象即可。那分库是不是要定义很多个 IFreeSql 对象?答案是的。
个别思路能够定义 static ConcurrentDictionary<string, IFreeSql> 存储所有 IFreeSql 对象(key = ConnectionString),当进行 CRUD 时获取到对应的 IFreeSql 即可。因为 IFreeSql 是动态单例设计长驻内存,分库数量太多的时候会浪费资源,因为不是所有分库都始终始终在拜访。例如租户分库 10000 个,定义 10000 个 static IFreeSql?
更好的方法能够应用 IdleBus 闲暇对象治理容器,无效组织对象反复利用,主动创立、销毁,解决【实例】过多且长时间占用的问题。有时候想做一个单例对象重复使用晋升性能,然而定义多了,有的又可能始终闲暇着占用资源。专门解决:又想反复利用,又想少占资源的场景。https://github.com/2881099/Id…
dotnet add package IdleBus
static IdleBus<IFreeSql> ib = new IdleBus<IFreeSql>(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<T>().Limit(10).ToList();
IdleBus 也是【单例】设计!次要的两个办法,注册,获取。idlebus 注册不是创立 IFreeSql,首次 Get 时才创立,前面会始终用曾经创立的。还有一个超时机制,如果 10 分钟该 IFreeSql 未应用会被 Dispose,而后下一次又会创立新的 IFreeSql,如此重复。从而解决了 10000 个 IFreeSql 长驻内存的问题。
还利用 AsyncLocal 个性扩大应用起来更加不便:
public static class IdleBusExtesions
{static AsyncLocal<string> asyncDb = new AsyncLocal<string>();
public static IdleBus<IFreeSql> ChangeDatabase(this IdleBus<IFreeSql> ib, string db)
{
asyncDb.Value = db;
return ib;
}
public static IFreeSql Get(this IdleBus<IFreeSql> ib) => ib.Get(asyncDb.Value ?? "db1");
public static IBaseRepository<T> GetRepository<T>(this IdleBus<IFreeSql> ib) where T : class
=> ib.Get().GetRepository<T>();
}
- 应用 ChangeDatabase 切换 db;
- 应用 Get() 获取以后 IFreeSql,省略每次都传递 db 参数;
- 应用 GetRepository 获取以后 IFreeSql 对应的仓储类;
留神:应用 IdleBus 须要弱化 IFreeSql 的存在,每次都应用 ib.Get 获取 IFreeSql 对象;
IdleBus<IFreeSql> ib = ...; // 单例注入
var fsql = ib.Get(); // 获取以后租户对应的 IFreeSql
var fsql00102 = ib.ChangeDatabase("db2").Get(); // 切换租户,前面的操作都是针对 db2
var songRepository = ib.GetRepository<Song>();
var detailRepository = ib.GetRepository<Detail>();
目前这种算是比较简单入门的计划,远不迭 mycat、sharding-jdbc 那么智能,比方:没有实现跨库事务。
写在最初
.NET 生态还处于较弱的状态,呐喊大家反对、踊跃参加开源我的项目,为下一个 .NET 开源社区五年计划做奉献。
心愿正在应用的、凶恶的您能动一动小手指,把文章转发一下,让更多人晓得 .NET 有这样一个好用的 ORM 存在。谢谢了!!
FreeSql 开源协定 MIT https://github.com/dotnetcore/FreeSql,能够商用,文档齐全。QQ 群:4336577(已满)、8578575(在线)、52508226(在线)
如果你有好的 ORM 实现想法,欢送给作者留言探讨,谢谢观看!