基於efcore的分表組件開源


ShardingCore

ShardingCore 是一個支持efcore 2.x 3.x 5.x的一個對於數據庫分表的一個簡易擴展,當然也支持不分表的普通使用,.Net下並沒有類似mycat或者sharding-jdbc之類的開源組件或者說有但是並沒有非常適用的或者說個人在用過后有一些地方因為限制沒法很好使用所以決定自己開發這個庫,目前該庫暫未支持分庫(未來會支持),僅支持分表,該項目的理念是讓你可以已最少的代碼量來實現自動分表的實現,經過多個開源項目的摸索參考目前正式開源本項目
項目地址 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
  • [提供多種默認分表規則路由] 按時間按取模,自定義(AbstractShardingOperatorVirtualRoute<T, TKey>)
  • [針對分頁進行優化] 大頁數跳轉支持低內存流式處理

缺點

  • [暫不支持分庫(不久后會支持)]
  • [消耗連接]出現分表與分表對象進行join如果條件沒法索引到具體表會生成笛卡爾積導致連接數爆炸,后期會進行針對該情況的配置
  • [該庫比較年輕] 可能會有一系列bug或者單元測試不到位的情況,但是只要你在群里或者提了issues我會盡快解決

安裝

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

配置

配置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)
        {
        }
    }
  • 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
使用時需要注意

  • 實體對象是否繼承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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM