sharding/README.md

314 lines
13 KiB
Markdown
Raw Normal View History

2021-01-26 10:49:53 +08:00
<h1 align="center"> ShardingCore </h1>
2021-01-26 22:18:07 +08:00
2021-01-26 22:13:28 +08:00
`ShardingCore` 是一个支持efcore 2.x 3.x 5.x的一个对于数据库分表的一个简易扩展,
2021-01-26 10:49:53 +08:00
目前该库暂未支持分库(未来会支持),仅支持分表,该项目的理念是让你可以已最少的代码量来实现自动分表的实现,经过多个开源项目的摸索参考目前正式开源本项目
2021-02-02 21:44:47 +08:00
项目地址 [github](https://github.com/xuejmnet/sharding-core) 喜欢的朋友可以点下star Thanks♪(・ω・)ノ
2021-01-26 10:49:53 +08:00
2021-01-26 22:13:28 +08:00
### 依赖
2021-01-26 22:28:50 +08:00
Release | EF Core | .NET Standard | .NET (Core) | Sql Server | Pomelo.EntityFrameworkCore.MySql
--- | --- | --- | --- | --- | ---
2021-02-02 21:44:47 +08:00
[5.x.x.x](https://www.nuget.org/packages/ShardingCore/5.0.0.3) | >= 5.0.x | 2.1 | 3.0+ | >= 2012 | 5.0.0-alpha.2
[3.x.x.x](https://www.nuget.org/packages/ShardingCore/3.0.0.3) | 3.1.10 | 2.0 | 2.0+ | >= 2012 | 3.2.4
[2.x.x.x](https://www.nuget.org/packages/ShardingCore/2.0.0.3) | 2.2.6 | 2.0 | 2.0+ | >= 2008 | 2.2.6
2021-01-26 22:13:28 +08:00
2021-01-26 10:49:53 +08:00
- [开始](#开始)
2021-02-02 21:44:47 +08:00
- [简介](#简介)
2021-01-26 22:13:28 +08:00
- [概念](#概念)
2021-02-02 21:44:47 +08:00
- [优点](#优点)
- [缺点](#缺点)
2021-01-26 22:15:41 +08:00
- [安装](#安装)
2021-01-26 10:49:53 +08:00
- [配置](#配置)
- [使用](#使用)
2021-01-27 13:38:30 +08:00
- [默认路由](#默认路由)
2021-01-26 22:13:28 +08:00
- [Api](#Api)
2021-01-26 10:49:53 +08:00
- [高级配置](#高级配置)
2021-01-26 22:13:28 +08:00
- [手动路由](#手动路由)
- [自动建表](#自动建表)
2021-01-26 22:35:41 +08:00
- [事务](#事务)
- [批量操作](#批量操作)
2021-01-26 10:49:53 +08:00
- [注意事项](#注意事项)
2021-01-26 22:35:41 +08:00
- [计划(Future)](#计划)
2021-01-27 13:38:30 +08:00
- [最后](#最后)
2021-01-26 22:13:28 +08:00
# 开始
2021-02-02 21:44:47 +08:00
以下所有例子都以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](https://github.com/xuejmnet/sharding-core/blob/main/src/ShardingCore/TableCreator/IShardingTableCreator.cs)
作为建表的依赖,如果需要可以参考 [按天自动建表](https://github.com/xuejmnet/sharding-core/tree/main/samples/Samples.AutoByDate.SqlServer)
2021-01-26 22:13:28 +08:00
## 概念
本库的几个简单的核心概念:
- [Tail]
2021-01-27 13:38:30 +08:00
尾巴、后缀物理表的后缀
2021-01-26 22:13:28 +08:00
- [TailPrefix]
2021-01-26 22:22:08 +08:00
尾巴前缀虚拟表和物理表的后缀中间的字符
2021-01-26 22:13:28 +08:00
- [物理表]
2021-02-02 21:44:47 +08:00
顾名思义就是数据库对应的实际表信息,表名(tablename+ tailprefix+ tail) [IPhysicTable](https://github.com/xuejmnet/sharding-core/blob/main/src/ShardingCore/Core/PhysicTables/IPhysicTable.cs)
2021-01-26 22:13:28 +08:00
- [虚拟表]
虚拟表就是系统将所有的物理表在系统里面进行抽象的一个总表对应到程序就是一个entity[IVirtualTable](https://github.com/xuejmnet/sharding-core/blob/main/src/ShardingCore/Core/VirtualTables/IVirtualTable.cs)
- [虚拟路由]
虚拟路由就是联系虚拟表和物理表的中间介质,虚拟表在整个程序中只有一份,那么程序如何知道要查询系统哪一张表呢,最简单的方式就是通过虚拟表对应的路由[IVirtualRoute](https://github.com/xuejmnet/sharding-core/blob/main/src/ShardingCore/Core/VirtualRoutes/IVirtualRoute.cs)
,由于基本上所有的路由都是和业务逻辑相关的所以虚拟路由由用户自己实现,该框架提供一个高级抽象
2021-02-02 21:44:47 +08:00
## 优点
- [支持自定义分表规则]
- [支持任意类型分表key]
- [针对iqueryable的扩展方便使用]
- [支持分表下的连表] ```join```
- [支持针对批处理的使用] ``` BulkInsert、BulkUpdate、BulkDelete```
- [提供多种默认分表规则路由] 按时间按取模
- [针对分页进行优化] 大页数跳转支持低内存流式处理
## 缺点
- [暂不支持分库(不久后会支持)]
- [消耗连接]出现分表与分表对象进行join如果条件没法索引到具体表会生成```笛卡尔积```导致连接数爆炸,后期会进行针对该情况的配置
- [该库比较年轻] 可能会有一系列bug或者单元测试不到位的情况,但是只要你在群里或者提了issues我会尽快解决
2021-01-26 22:15:41 +08:00
## 安装
```xml
2021-02-02 21:44:47 +08:00
<PackageReference Include="ShardingCore.SqlServer" Version="5.0.0.3" />
2021-01-26 22:15:41 +08:00
```
2021-01-26 22:13:28 +08:00
## 配置
2021-01-27 13:38:30 +08:00
配置entity 推荐 [fluent api](https://docs.microsoft.com/en-us/ef/core/modeling/) 可以实现自动建表功能
2021-01-26 22:22:08 +08:00
`IShardingEntity`数据库对象必须继承该接口
`ShardingKey`分表字段需要使用该特性
```c#
2021-02-02 21:44:47 +08:00
public class SysUserMod:IShardingEntity
2021-01-26 22:22:08 +08:00
{
/// <summary>
2021-02-02 21:44:47 +08:00
/// 用户Id用于分表
2021-01-26 22:22:08 +08:00
/// </summary>
2021-02-02 21:44:47 +08:00
[ShardingKey]
2021-01-26 22:22:08 +08:00
public string Id { get; set; }
/// <summary>
2021-02-02 21:44:47 +08:00
/// 用户名称
2021-01-26 22:22:08 +08:00
/// </summary>
public string Name { get; set; }
/// <summary>
2021-02-02 21:44:47 +08:00
/// 用户姓名
2021-01-26 22:22:08 +08:00
/// </summary>
public int Age { get; set; }
}
2021-02-02 21:44:47 +08:00
public class SysUserModMap:IEntityTypeConfiguration<SysUserMod>
2021-01-26 22:22:08 +08:00
{
2021-02-02 21:44:47 +08:00
public void Configure(EntityTypeBuilder<SysUserMod> builder)
2021-01-26 22:22:08 +08:00
{
builder.HasKey(o => o.Id);
builder.Property(o => o.Id).IsRequired().HasMaxLength(128);
builder.Property(o => o.Name).HasMaxLength(128);
2021-02-02 21:44:47 +08:00
builder.ToTable(nameof(SysUserMod));
2021-01-26 22:22:08 +08:00
}
}
```
2021-02-02 21:44:47 +08:00
创建virtual route
2021-01-26 22:13:28 +08:00
实现 `AbstractShardingOperatorVirtualRoute<T, TKey>`
2021-02-02 21:44:47 +08:00
抽象,或者实现系统默认的虚拟路由
2021-01-27 13:38:30 +08:00
框架默认有提供几个简单的路由 [默认路由](#默认路由)
2021-01-26 22:13:28 +08:00
```c#
2021-02-02 21:44:47 +08:00
public class SysUserModVirtualRoute : AbstractSimpleShardingModKeyStringVirtualRoute<SysUserMod>
2021-01-26 22:13:28 +08:00
{
2021-02-02 21:44:47 +08:00
public SysUserModVirtualRoute() : base(3)
{
}
public override List<string> GetAllTails()
{
return new() { "0","1","2"};
}
}
2021-01-26 22:13:28 +08:00
```
- `GetAllTails`
现在数据库已存在的尾巴有哪些
`Startup.cs` 下的 `ConfigureServices(IServiceCollection services)`
```c#
2021-02-02 21:44:47 +08:00
services.AddShardingSqlServer(o =>
2021-01-26 22:13:28 +08:00
{
2021-02-02 21:44:47 +08:00
o.ConnectionString = "";
o.AddSharding<SysUserModVirtualRoute>();
o.UseShardingCoreConfig((provider, config) =>
2021-01-26 22:13:28 +08:00
{
2021-02-02 21:44:47 +08:00
//如果是development就判断并且新建数据库如果不存在的话(ishardingentity不会被创建)
config.EnsureCreated = provider.GetService<IHostEnvironment>().IsDevelopment();
//ishardingentity表是否需要在启动时创建(如果已创建可以选择不创建)
config.CreateShardingTableOnStart = true;
2021-01-26 22:13:28 +08:00
});
});
```
`Startup.cs` 下的 ` Configure(IApplicationBuilder app, IWebHostEnvironment env)` 你也可以自行封装[app.UseShardingCore()](https://github.com/xuejmnet/sharding-core/blob/main/samples/Sample.SqlServer/DIExtension.cs)
```c#
var shardingBootstrapper = app.ApplicationServices.GetRequiredService<IShardingBootstrapper>();
shardingBootstrapper.Start();
```
## 使用
```c#
private readonly IVirtualDbContext _virtualDbContext;
public ctor(IVirtualDbContext virtualDbContext)
{
_virtualDbContext = virtualDbContext;
}
public async Task ToList_All()
{
2021-02-02 21:44:47 +08:00
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();
2021-01-26 22:13:28 +08:00
}
```
2021-02-02 21:44:47 +08:00
更多操作可以参考单元测试
2021-01-26 22:13:28 +08:00
## Api
2021-01-28 17:22:06 +08:00
方法 | Method | [SqlServer Unit Test](https://github.com/xuejmnet/sharding-core/blob/main/test/ShardingCore.Test50/ShardingTest.cs) | [MySql Unit Test](https://github.com/xuejmnet/sharding-core/blob/main/test/ShardingCore.Test50.MySql/ShardingTest.cs)
2021-01-27 13:38:30 +08:00
--- |--- |--- |---
2021-02-01 21:49:49 +08:00
获取集合 |ToShardingListAsync |yes |yes
第一条 |ShardingFirstOrDefaultAsync |yes |yes
最大 |ShardingMaxAsync |yes |yes
最小 |ShardingMinAsync |yes |yes
是否存在 |ShardingAnyAsync |yes |yes
分页 |ToShardingPageResultAsync |yes |yes
数目 |ShardingCountAsync |yes |yes
求和 |ShardingSumAsync |yes |yes
2021-02-02 21:44:47 +08:00
分组 |ShardingGroupByAsync |yes |yes
2021-01-27 13:38:30 +08:00
## 默认路由
抽象abstract | 路由规则 | tail | 索引
--- |--- |--- |---
2021-01-27 13:40:25 +08:00
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`
2021-01-27 13:38:30 +08:00
注:`contains`表示为`o=>ids.contains(o.shardingkey)`
#高级
2021-02-02 21:44:47 +08:00
## 批量操作
批量操作将对应的dbcontext和数据进行分离由用户自己选择第三方框架比如zzz进行批量操作或者batchextension
```c#
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);
});
```
2021-02-02 21:44:47 +08:00
## 手动路由
```c#
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();
```
## 自动建表
[参考](https://github.com/xuejmnet/sharding-core/tree/main/samples/Samples.AutoByDate.SqlServer)
## 事务
默认savechanges支持事务如果需要where.update需要手动开启事务
```c#
_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<T>使用asnotracking所以基本不支持跟踪,目前框架采用AppDomain.CurrentDomain.GetAssemblies();
可能会导致程序集未被加载所以尽可能在api层加载所需要的dll
# 计划
- [提供官网如果该项目比较成功的话]
- [开发更完善的文档]
- [支持分库]
- [支持更多数据库查询]
2021-01-27 13:38:30 +08:00
# 最后
凭借各大开源生态圈提供的优秀代码和思路才有的这个框架,希望可以为.Net生态提供一份微薄之力,该框架本人会一直长期维护,有大神技术支持可以联系下方方式欢迎star :)
QQ群:771630778
个人QQ:326308290(欢迎技术支持提供您宝贵的意见)
个人邮箱:326308290@qq.com