基于efcore的分表组件开源
ShardingCore
ShardingCore
是一个支持efcore 2.x 3.x 5.x的一个对于数据库分表的一个简易扩展,
目前该库暂未支持分库(未来会支持),仅支持分表,该项目的理念是让你可以已最少的代码量来实现自动分表的实现,经过多个开源项目的摸索参考目前正式开源本项目
项目地址 github 喜欢的朋友可以点下star Thanks♪(・ω・)ノ
依赖
Release | EF Core | .NET Standard | .NET (Core) | Sql Server | Pomelo.EntityFrameworkCore.MySql |
---|---|---|---|---|---|
5.x.x.x | >= 5.0.x | 2.1 | 3.0+ | >= 2012 | 5.0.0-alpha.2 |
3.x.x.x | 3.1.10 | 2.0 | 2.0+ | >= 2012 | 3.2.4 |
2.x.x.x | 2.2.6 | 2.0 | 2.0+ | >= 2008 | 2.2.6 |
开始
以下所有例子都以Sql Server为例 MySql亦如此
简介
目前该库处于初期阶段,有很多bug也希望各位多多理解,一起努力为.net生态做出一份微薄之力,目前该库支持的分页可以进行完全的自定义,基本上可以满足95%以上的
业务需求,唯一的限制就是分表规则必须满足 x+y+z,x表示固定的表名,y表示固定的表名和表后缀之间的联系(可以为空),z表示表后缀,可以按照你自己的任意业务逻辑进行切分,
如:user_0,user_1或者user202101,user202102...当然该库同样适用于多租户模式下的隔离,该库为了支持之后的分库已经重写了之前的union all查询模式,并且支持多种api,
支持多种查询包括join group by max count min avg sum ...等一系列查询,之后可能会添加更多支持,目前该库的使用非常简单,基本上就是针对IQueryable的扩展,为了保证
该库的简介目前仅使用该库无法或者说难以实现自动建表,但是只需要配合定时任务该库即可完成24小时无人看管自动管理。该库提供了 IShardingTableCreator
作为建表的依赖,如果需要可以参考 按天自动建表
概念
本库的几个简单的核心概念:
- [Tail]
尾巴、后缀物理表的后缀 - [TailPrefix]
尾巴前缀虚拟表和物理表的后缀中间的字符 - [物理表]
顾名思义就是数据库对应的实际表信息,表名(tablename+ tailprefix+ tail) IPhysicTable - [虚拟表]
虚拟表就是系统将所有的物理表在系统里面进行抽象的一个总表对应到程序就是一个entityIVirtualTable - [虚拟路由]
虚拟路由就是联系虚拟表和物理表的中间介质,虚拟表在整个程序中只有一份,那么程序如何知道要查询系统哪一张表呢,最简单的方式就是通过虚拟表对应的路由IVirtualRoute
,由于基本上所有的路由都是和业务逻辑相关的所以虚拟路由由用户自己实现,该框架提供一个高级抽象
优点
- [支持自定义分表规则]
- [支持任意类型分表key]
- [针对iqueryable的扩展方便使用]
- [支持分表下的连表]
join
- [支持针对批处理的使用]
BulkInsert、BulkUpdate、BulkDelete
- [提供多种默认分表规则路由] 按时间按取模
- [针对分页进行优化] 大页数跳转支持低内存流式处理
缺点
- [暂不支持分库(不久后会支持)]
- [消耗连接]出现分表与分表对象进行join如果条件没法索引到具体表会生成
笛卡尔积
导致连接数爆炸,后期会进行针对该情况的配置 - [该库比较年轻] 可能会有一系列bug或者单元测试不到位的情况,但是只要你在群里或者提了issues我会尽快解决
安装
<PackageReference Include="ShardingCore.SqlServer" Version="5.0.0.3" />
配置
配置entity 推荐 fluent api 可以实现自动建表功能
IShardingEntity
数据库对象必须继承该接口
ShardingKey
分表字段需要使用该特性
public class SysUserMod:IShardingEntity
{
/// <summary>
/// 用户Id用于分表
/// </summary>
[ShardingKey]
public string Id { get; set; }
/// <summary>
/// 用户名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 用户姓名
/// </summary>
public int Age { get; set; }
}
public class SysUserModMap:IEntityTypeConfiguration<SysUserMod>
{
public void Configure(EntityTypeBuilder<SysUserMod> builder)
{
builder.HasKey(o => o.Id);
builder.Property(o => o.Id).IsRequired().HasMaxLength(128);
builder.Property(o => o.Name).HasMaxLength(128);
builder.ToTable(nameof(SysUserMod));
}
}
创建virtual route
实现 AbstractShardingOperatorVirtualRoute<T, TKey>
抽象,或者实现系统默认的虚拟路由
框架默认有提供几个简单的路由 默认路由
public class SysUserModVirtualRoute : AbstractSimpleShardingModKeyStringVirtualRoute<SysUserMod>
{
public SysUserModVirtualRoute() : base(3)
{
}
public override List<string> GetAllTails()
{
return new() { "0","1","2"};
}
}
GetAllTails
现在数据库已存在的尾巴有哪些
Startup.cs
下的 ConfigureServices(IServiceCollection services)
services.AddShardingSqlServer(o =>
{
o.ConnectionString = "";
o.AddSharding<SysUserModVirtualRoute>();
o.UseShardingCoreConfig((provider, config) =>
{
//如果是development就判断并且新建数据库如果不存在的话(ishardingentity不会被创建)
config.EnsureCreated = provider.GetService<IHostEnvironment>().IsDevelopment();
//ishardingentity表是否需要在启动时创建(如果已创建可以选择不创建)
config.CreateShardingTableOnStart = true;
});
});
Startup.cs
下的 Configure(IApplicationBuilder app, IWebHostEnvironment env)
你也可以自行封装app.UseShardingCore()
var shardingBootstrapper = app.ApplicationServices.GetRequiredService<IShardingBootstrapper>();
shardingBootstrapper.Start();
使用
private readonly IVirtualDbContext _virtualDbContext;
public ctor(IVirtualDbContext virtualDbContext)
{
_virtualDbContext = virtualDbContext;
}
public async Task ToList_All()
{
//查询list集合
var all=await _virtualDbContext.Set<SysUserMod>().ToShardingListAsync();
//链接查询
var list = await (from u in _virtualDbContext.Set<SysUserMod>()
join salary in _virtualDbContext.Set<SysUserSalary>()
on u.Id equals salary.UserId
select new
{
Salary = salary.Salary,
DateOfMonth = salary.DateOfMonth,
Name = u.Name
}).ToShardingListAsync();
//聚合查询
var ids = new[] {"200", "300"};
var dateOfMonths = new[] {202111, 202110};
var group = await (from u in _virtualDbContext.Set<SysUserSalary>()
.Where(o => ids.Contains(o.UserId) && dateOfMonths.Contains(o.DateOfMonth))
group u by new
{
UId = u.UserId
}
into g
select new
{
GroupUserId = g.Key.UId,
Count = g.Count(),
TotalSalary = g.Sum(o => o.Salary),
AvgSalary = g.Average(o => o.Salary),
MinSalary = g.Min(o => o.Salary),
MaxSalary = g.Max(o => o.Salary)
}).ToShardingListAsync();
}
更多操作可以参考单元测试
Api
方法 | Method | SqlServer Unit Test | MySql Unit Test |
---|---|---|---|
获取集合 | ToShardingListAsync | yes | yes |
第一条 | ShardingFirstOrDefaultAsync | yes | yes |
最大 | ShardingMaxAsync | yes | yes |
最小 | ShardingMinAsync | yes | yes |
是否存在 | ShardingAnyAsync | yes | yes |
分页 | ToShardingPageResultAsync | yes | yes |
数目 | ShardingCountAsync | yes | yes |
求和 | ShardingSumAsync | yes | yes |
分组 | ShardingGroupByAsync | yes | yes |
默认路由
抽象abstract | 路由规则 | tail | 索引 |
---|---|---|---|
AbstractSimpleShardingModKeyIntVirtualRoute | 取模 | 0,1,2... | = |
AbstractSimpleShardingModKeyStringVirtualRoute | 取模 | 0,1,2... | = |
AbstractSimpleShardingDayKeyDateTimeVirtualRoute | 按时间 | yyyyMMdd | >,>=,<,<=,=,contains |
AbstractSimpleShardingDayKeyLongVirtualRoute | 按时间戳 | yyyyMMdd | >,>=,<,<=,=,contains |
AbstractSimpleShardingWeekKeyDateTimeVirtualRoute | 按时间 | yyyyMMdd_dd | >,>=,<,<=,=,contains |
AbstractSimpleShardingWeekKeyLongVirtualRoute | 按时间戳 | yyyyMMdd_dd | >,>=,<,<=,=,contains |
AbstractSimpleShardingMonthKeyDateTimeVirtualRoute | 按时间 | yyyyMM | >,>=,<,<=,=,contains |
AbstractSimpleShardingMonthKeyLongVirtualRoute | 按时间戳 | yyyyMM | >,>=,<,<=,=,contains |
AbstractSimpleShardingYearKeyDateTimeVirtualRoute | 按时间 | yyyy | >,>=,<,<=,=,contains |
AbstractSimpleShardingYearKeyLongVirtualRoute | 按时间戳 | yyyy | >,>=,<,<=,=,contains |
注:contains
表示为o=>ids.contains(o.shardingkey)
高级
批量操作
批量操作将对应的dbcontext和数据进行分离由用户自己选择第三方框架比如zzz进行批量操作或者batchextension
virtualDbContext.BulkInsert<SysUserMod>(new List<SysUserMod>())
.BatchGroups.ForEach(pair =>
{
///zzz or other
pair.Key.BlukInsert(pair.Value);
});
var shardingBatchUpdateEntry = virtualDbContext.BulkUpdate<SysUserMod>(o => o.Id == "1", o => new SysUserMod()
{
Name = "name_01"
});
shardingBatchUpdateEntry.DbContexts.ForEach(context =>
{
//zzz or other
context.Where(shardingBatchUpdateEntry.Where).Update(shardingBatchUpdateEntry.UpdateExp);
});
手动路由
var shardingQueryable = _virtualDbContext.Set<SysUserMod>().AsSharding();
//禁用自动路由
shardingQueryable.DisableAutoRouteParse();
//添加路由直接查询尾巴0的表
shardingQueryable.AddManualRoute<SysUserMod>("0");
//添加路由针对该条件的路由
shardingQueryable.AddManualRoute<SysUserMod>(o=>o.Id=="100");
var list=await shardingQueryable.ToListAsync();
自动建表
事务
默认savechanges支持事务如果需要where.update需要手动开启事务
_virtualDbContext.BeginTransaction();
var shardingBatchUpdateEntry = _virtualDbContext.BulkUpdate<SysUserMod>(o=>o.Id=="123",o=>new SysUserMod()
{
Name = "name_modify"
});
foreach (var dbContext in shardingBatchUpdateEntry.DbContexts)
{
//zzz or other batch
}
await _virtualDbContext.SaveChangesAsync();
注意事项
该库的IVirtualDbContext.Set使用asnotracking所以基本不支持跟踪,目前框架采用AppDomain.CurrentDomain.GetAssemblies();
可能会导致程序集未被加载所以尽可能在api层加载所需要的dll
计划
- [提供官网如果该项目比较成功的话]
- [开发更完善的文档]
- [支持分库]
- [支持更多数据库查询]
最后
理论上该库的思想可以解决大部分orm的分表,目前是仅针对efcore的后期如果可以获取也会对其他orm进行sharding库的开发
该框架借鉴了大部分分表组件的思路,目前提供的接口都已经实现,并且支持跨表查询,基于分页查询该框架也使用了流式查询保证不会再skip大数据的时候内存会爆炸,至于groupby目前已经在开发支持了,相信不久后就会发布新版本,目前这个库只是一个刚刚成型的库还有很多不完善的地方希望大家多多包涵,如果喜欢的话也希望大家给个star.
该文档是我晚上赶工赶出来的也想趁热打铁希望更多的人关注,也希望更多的人可以交流。
凭借各大开源生态圈提供的优秀代码和思路才有的这个框架,希望可以为.Net生态提供一份微薄之力,该框架本人会一直长期维护,有大神技术支持可以联系下方方式欢迎star
QQ群:771630778
个人QQ:326308290(欢迎技术支持提供您宝贵的意见)
个人邮箱:326308290@qq.com
基于efcore的分表组件开源的更多相关文章
- .NET 5 全自动分表组件,.NET 分表方案 ,分表架构与设计
一.疑问&目的 1.1 分表使用场景 (1)可扩展架构设计,比如一个ERP用5年不卡,到了10就卡了因为数据太多了,这个时候很多人都是备份然后清空数据,这个工作大并且麻烦,以前的数据很难在使用 ...
- 架构组件:基于Shard-Jdbc分库分表,数据库扩容方案
本文源码:GitHub·点这里 || GitEE·点这里 一.数据库扩容 1.业务场景 互联网项目中有很多"数据量大,业务复杂度高,需要分库分表"的业务场景. 这样分层的架构 (1 ...
- Spring-boot2X基于sharding-jdbc3.x分表分库
ShardingSphere是一套开源的分布式数据库中间件解决方案组成的生态圈,它由Sharding-JDBC.Sharding-Proxy和Sharding-Sidecar(计划中)这3款相互独立的 ...
- 数据量大了一定要分表,分库分表组件Sharding-JDBC入门与项目实战
最近项目中不少表的数据量越来越大,并且导致了一些数据库的性能问题.因此想借助一些分库分表的中间件,实现自动化分库分表实现.调研下来,发现Sharding-JDBC目前成熟度最高并且应用最广的Java分 ...
- TSharding:用于蘑菇街交易平台的分库分表组件
tsharding TSharding is the simple sharding component used in mogujie trade platform. 分库分表业界方案 分库分表TS ...
- efcore分表下"完美"实现
ShardingCore 如何呈现"完美"分表 这篇文章是我针对efcore的分表的简单介绍,如果您有以下需求那么可以自己选择是否使用本框架,本框架将一直持续更新下去,并且免费开源 ...
- efcore分表分库原理解析
ShardingCore ShardingCore 易用.简单.高性能.普适性,是一款扩展针对efcore生态下的分表分库的扩展解决方案,支持efcore2+的所有版本,支持efcore2+的所有数据 ...
- 基于SpringCloud实现Shard-Jdbc的分库分表模式,数据库扩容方案
本文源码:GitHub·点这里 || GitEE·点这里 一.项目结构 1.工程结构 2.模块命名 shard-common-entity: 公共代码块 shard-open-inte: 开放接口管理 ...
- mysql、oracle分库分表方案之sharding-jdbc使用(非demo示例)
选择开源核心组件的一个非常重要的考虑通常是社区活跃性,一旦项目团队无法进行自己后续维护和扩展的情况下更是如此. 至于为什么选择sharding-jdbc而不是Mycat,可以参考知乎讨论帖子https ...
随机推荐
- Vs2017编译器提示:不能将“const char *”类型的值分配到“char *”类型的实体
在项目属性中将语言符合模式改成否即可
- 杭电OJ2010---水仙花数(c++)(方法:输出几个数之间用空格隔开,最后一个数没有空格)
水仙花数 Problem Description 春天是鲜花的季节,水仙花就是其中最迷人的代表,数学上有个水仙花数,他是这样定义的: "水仙花数"是指一个三位数,它的各位数字的立方 ...
- FastApi学习(一)
前言 学习不止 正文 介绍 FastApi是PythonWeb框架的'新晋干员',虽然年轻但是很能打 目前已有 12k start GitHub 官网 为什么说他能打呢?它内部使用了 Python 的 ...
- 【MyBatis】MyBatis 动态 SQL
MyBatis 动态SQL if 可以根据实体类的不同取值,使用不同的 SQL 语句来进行查询. 使用动态 SQL 最常见情景是根据条件包含 where 子句的一部分. 持久层 DAO 接口: pub ...
- innodb是怎么刷新日志缓冲的
当innodb把日志缓冲刷新到磁盘日志文件的时候,先会用一个mutex锁住缓冲区,刷新到所需要的位置,然后移动剩下的条目到缓冲区的前面,当mutex释放时,可能有超过一个事务已经准备好刷新其日志记录, ...
- 【Linux】rsync模板配置问题
------------------------------------------------------------------------------------------------- | ...
- 【Linux】salt的cmd.script命令介绍
salt是一个很棒的自动化运维工具之一,常用的有cmd.run,今天介绍的是cmd.script 其实一眼就能看出这个命令是执行脚本的命令 具体操作如下: 1.将/etc/salt/master中的 ...
- 【Oracle】修改列的大小
alter table 表名 modify column_name varchar2(32) alter table 表名 modify (column_name1 varchar(20) defa ...
- 敏捷史话(四):敏捷是人的天性 —— Arie van Bennekum
敏捷是人的天性,是你与生俱来的东西.面对敏捷,Arie van Bennekum 下了这样一个结论. 但这并不意味着人们只能通过天赋获得敏捷,对于想要学习敏捷的人来说,敏捷绝不是仅仅靠学习僵化的框架. ...
- 入门OJ:扫雪
扫雪1 题目描述 大雪履盖了整个城市,市政府要求冬季服务部门尽快将一些街道(列在一份清单中)的积雪清除掉以恢复交通,整个城市由许多交叉路口和街道构成,当然任意两个交叉路口都是直接或间接连通的,清单给出 ...