基於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

相關文章
相關標籤/搜索