基于efcore的分表组件开源

ShardingCore

ShardingCore 是一个支持efcore 2.x 3.x 5.x的一个对于数据库分表的一个简易扩展,固然也支持不分表的普通使用,.Net下并无相似mycat或者sharding-jdbc之类的开源组件或者说有可是并无很是适用的或者说我的在用事后有一些地方由于限制无法很好使用因此决定本身开发这个库,目前该库暂未支持分库(将来会支持),仅支持分表,该项目的理念是让你能够已最少的代码量来实现自动分表的实现,通过多个开源项目的摸索参考目前正式开源本项目
项目地址 github 喜欢的朋友能够点下star Thanks♪(・ω・)ノgit

依赖

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亦如此github

简介

目前该库处于初期阶段,有不少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
做为建表的依赖,若是须要能够参考 按天自动建表数据库

概念

本库的几个简单的核心概念:c#

  • [Tail]
    尾巴、后缀物理表的后缀
  • [TailPrefix]
    尾巴前缀虚拟表和物理表的后缀中间的字符
  • [物理表]
    顾名思义就是数据库对应的实际表信息,表名(tablename+ tailprefix+ tail) IPhysicTable
  • [虚拟表]
    虚拟表就是系统将全部的物理表在系统里面进行抽象的一个总表对应到程序就是一个entityIVirtualTable
  • [虚拟路由]
    虚拟路由就是联系虚拟表和物理表的中间介质,虚拟表在整个程序中只有一份,那么程序如何知道要查询系统哪一张表呢,最简单的方式就是经过虚拟表对应的路由IVirtualRoute
    ,因为基本上全部的路由都是和业务逻辑相关的因此虚拟路由由用户本身实现,该框架提供一个高级抽象

优势

  • [支持自定义分表规则]
  • [支持任意类型分表key]
  • [针对iqueryable的扩展方便使用]
  • [支持分表下的连表] join
  • [支持针对批处理的使用] BulkInsert、BulkUpdate、BulkDelete
  • [提供多种默认分表规则路由] 按时间按取模,自定义(AbstractShardingOperatorVirtualRoute<T, TKey>)
  • [针对分页进行优化] 大页数跳转支持低内存流式处理

缺点

  • [暂不支持分库(不久后会支持)]
  • [消耗链接]出现分表与分表对象进行join若是条件无法索引到具体表会生成笛卡尔积致使链接数爆炸,后期会进行针对该状况的配置
  • [该库比较年轻] 可能会有一系列bug或者单元测试不到位的状况,可是只要你在群里或者提了issues我会尽快解决

安装

<PackageReference Include="ShardingCore.SqlServer" Version="5.0.0.4" />

配置

配置entity 推荐 fluent api 能够实现自动建表功能
IShardingEntity数据库对象必须继承该接口
ShardingKey分表字段须要使用该特性api

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>
抽象,或者实现系统默认的虚拟路由
框架默认有提供几个简单的路由 默认路由app

public class SysUserModVirtualRoute : AbstractSimpleShardingModKeyStringVirtualRoute<SysUserMod>
    {
        public SysUserModVirtualRoute() : base(3)
        {
        }
    }
  • 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()async

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();
        }

更多操做能够参考单元测试ide

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
使用时须要注意

  • 实体对象是否继承IShardingEntity
  • 实体对象是否有ShardingKey
  • 实体对象是否已经实现了一个虚拟路由
  • startup是否已经添加虚拟路由
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
var shardingBootstrapper = app.ApplicationServices.GetRequiredService<IShardingBootstrapper>();
  shardingBootstrapper.Start();

计划

  • [提供官网若是该项目比较成功的话]
  • [开发更完善的文档]
  • [支持分库]
  • [支持更多数据库查询]

最后

理论上该库的思想能够解决大部分orm的分表,目前是仅针对efcore的后期若是能够获取也会对其余orm进行sharding库的开发
该框架借鉴了大部分分表组件的思路,目前提供的接口都已经实现,而且支持跨表查询,基于分页查询该框架也使用了流式查询保证不会再skip大数据的时候内存会爆炸,至于groupby目前已经在开发支持了,相信不久后就会发布新版本,目前这个库只是一个刚刚成型的库还有不少不完善的地方但愿你们多多包涵,若是喜欢的话也但愿你们给个star.
该文档是我晚上赶工赶出来的也想趁热打铁但愿更多的人关注,也但愿更多的人能够交流。

凭借各大开源生态圈提供的优秀代码和思路才有的这个框架,但愿能够为.Net生态提供一份微薄之力,该框架本人会一直长期维护,有大神技术支持能够联系下方方式欢迎star 😃

博客

QQ群:771630778

我的QQ:326308290(欢迎技术支持提供您宝贵的意见)

我的邮箱:326308290@qq.com

相关文章
相关标签/搜索