ShardingCore 如何呈现“完美”分表
這篇文章是我針對(duì)efcore的分表的簡(jiǎn)單介紹,如果您有以下需求那么可以自己選擇是否使用本框架,本框架將一直持續(xù)更新下去,并且免費(fèi)開(kāi)源為.net生態(tài)做貢獻(xiàn),如果您覺(jué)得不錯(cuò)那么請(qǐng)幫忙點(diǎn)個(gè)star謝謝,框架地址[`sharding-core`](https://github.com/xuejmnet/sharding-core) 您的支持是對(duì)我最大的動(dòng)力。
如果您對(duì)分表有以下痛點(diǎn)那么不妨試試我這邊開(kāi)源的框架sharding-core?,是否需要無(wú)感知使用分表組件,是否需要支持abp,是否需要支持自定義分表規(guī)則,是否需要支持自定義分表鍵,是否需要支持特定的efcore版本,是否希望框架不帶任何三方框架干凈,是否需要支持讀寫(xiě)分離,是否需要?jiǎng)討B(tài)添加表,是否需要支持join,group等操作,是否需要支持追蹤特性,是否想在不修改原先代碼的基礎(chǔ)上擴(kuò)展分表功能,如果一起上幾個(gè)條件任意組合且你在市面上沒(méi)辦法找到可替代的框架可以試試本框架。如何使用代碼具體可以參考github 將代碼下載下來(lái)如果本地裝了sqlserver直接運(yùn)行單元測(cè)試或者Sample.SqlServer程序會(huì)自動(dòng)在本地新建數(shù)據(jù)庫(kù)新建數(shù)據(jù)庫(kù)表結(jié)構(gòu),目前初始化數(shù)據(jù)為用戶信息和用戶對(duì)應(yīng)的月薪信息表,用戶表以用戶id取模,用戶月薪表以月份分表。
首先需要了解本框架的一個(gè)版本號(hào)不然將對(duì)您的使用產(chǎn)生一定的分歧,目前框架分為3個(gè)版本分別是2.x,3.x,5.x3個(gè)版本,分別對(duì)應(yīng)efcore 2.x efcore 3.x efcore 5.x,有人要問(wèn)為什么不支持6.x呢(小弟剛剛在上周完成對(duì)本框架的開(kāi)發(fā)重構(gòu),目前還未對(duì)efcore 6.x進(jìn)行著手不過(guò)將在不遠(yuǎn)的將來(lái)即將支持(目測(cè)1-2個(gè)星期內(nèi)))。
目前efcore生態(tài)下有著許許多多的分表、分庫(kù)的解決方案,但是目前來(lái)講都有其不足點(diǎn),比如需要手動(dòng)設(shè)置分表后綴、需要大量替換現(xiàn)有代碼、不支持事務(wù)等等一系列問(wèn)題,所以在這個(gè)大前提下我之前開(kāi)源了sharding-core?分表組件,這個(gè)分表組件是目前來(lái)說(shuō)個(gè)人認(rèn)為比較“完美”的分表組件,這個(gè)分表組件目前是參考了sharding-jdbc來(lái)實(shí)現(xiàn)的,但是比sharding-jdbc更加強(qiáng)大(因?yàn)镃#的表達(dá)式)。首先我們來(lái)看下目前市面上有的分表組件的缺點(diǎn)我們來(lái)針對(duì)其缺點(diǎn)進(jìn)行痛點(diǎn)解決。
efcore支持情況
| 2.x | 支持 |
| 3.x | 支持 |
| 5.x | 支持 |
| 6.x | 即將支持 |
數(shù)據(jù)庫(kù)支持情況
| SqlServer | 支持 |
| MySql | 支持 |
| PostgreSql | 支持 |
| SQLite | 支持 |
| Oracle | 支持 |
| 其他 | 支持(只要efcore支持) |
理論上只要是efcore對(duì)應(yīng)版本支持的數(shù)據(jù)庫(kù),sharding-core都將支持。
如何開(kāi)始使用
1.創(chuàng)建一個(gè)數(shù)據(jù)庫(kù)對(duì)象繼承IShardingTable并且在對(duì)應(yīng)的分表字段上進(jìn)行[ShardingTableKey]特性的標(biāo)注
/// <summary>/// 用戶表/// </summary>public class SysUserMod : IShardingTable{/// <summary>/// 用戶Id用于分表/// </summary>[ShardingTableKey(TailPrefix = "_")]public string Id { get; set; }/// <summary>/// 用戶名稱/// </summary>public string Name { get; set; }/// <summary>/// 用戶姓名/// </summary>public int Age { get; set; }}2.創(chuàng)建對(duì)應(yīng)的實(shí)體表對(duì)應(yīng)配置 推薦?fluent api
public class SysTestMap:IEntityTypeConfiguration<SysTest>{public void Configure(EntityTypeBuilder<SysTest> builder){builder.HasKey(o => o.Id);builder.Property(o => o.Id).IsRequired().HasMaxLength(128);builder.Property(o => o.UserId).IsRequired().HasMaxLength(128);builder.ToTable(nameof(SysTest));}}3.創(chuàng)建對(duì)應(yīng)的分表規(guī)則 取模分表,參數(shù)2代表后綴2位就是00-99最多100張表,3表示模3== key.hashcode() %3
public class SysUserModVirtualTableRoute : AbstractSimpleShardingModKeyStringVirtualTableRoute<SysUserMod>{public SysUserModVirtualTableRoute() : base(2,3){}}4創(chuàng)建對(duì)應(yīng)執(zhí)行的dbcontext 這一步除了繼承IShardingTableDbContext外其他和普通dbcontext一樣
public class DefaultTableDbContext: DbContext,IShardingTableDbContext{public DefaultTableDbContext(DbContextOptions<DefaultTableDbContext> options) :base(options){}protected override void OnModelCreating(ModelBuilder modelBuilder){base.OnModelCreating(modelBuilder);modelBuilder.ApplyConfiguration(new SysUserModMap());}public IRouteTail RouteTail { get; set; }}5.添加分表dbcontext
public class DefaultShardingDbContext:AbstractShardingDbContext<DefaultTableDbContext>{public DefaultShardingDbContext(DbContextOptions<DefaultShardingDbContext> options) : base(options){}protected override void OnModelCreating(ModelBuilder modelBuilder){base.OnModelCreating(modelBuilder);modelBuilder.ApplyConfiguration(new SysUserModMap());}public override Type ShardingDbContextType => this.GetType();}6.添加配置
public void ConfigureServices(IServiceCollection services){services.AddControllers();//原先的dbcontext可以用也可以不用如果原先的dbcontext還在用就繼續(xù)//services.AddDbContext<DefaultTableDbContext>(o => o.UseSqlServer("Data Source=localhost;Initial Catalog=ShardingCoreDBxx3;Integrated Security=True"));services.AddShardingDbContext<DefaultShardingDbContext, DefaultTableDbContext>(o => o.UseSqlServer("Data Source=localhost;Initial Catalog=ShardingCoreDBxx2;Integrated Security=True;"), op =>{op.EnsureCreatedWithOutShardingTable = true;op.CreateShardingTableOnStart = true;op.UseShardingOptionsBuilder((connection, builder) => builder.UseSqlServer(connection).UseLoggerFactory(efLogger),//使用dbconnection創(chuàng)建dbcontext支持事務(wù)(conStr,builder) => builder.UseSqlServer(conStr).UseLoggerFactory(efLogger));//使用鏈接字符串創(chuàng)建dbcontextop.AddShardingTableRoute<SysUserModVirtualTableRoute>();});}public void Configure(IApplicationBuilder app, IWebHostEnvironment env){...//添加啟動(dòng)項(xiàng)app.UseShardingCore();...}public static class ShardingCoreExtension{public static IApplicationBuilder UseShardingCore(this IApplicationBuilder app){var shardingBootstrapper = app.ApplicationServices.GetRequiredService<IShardingBootstrapper>();shardingBootstrapper.Start();return app;}}7.控制器使用
private readonly DefaultShardingDbContext _defaultTableDbContext;public ValuesController(DefaultShardingDbContext defaultTableDbContext){_defaultTableDbContext = defaultTableDbContext;}[HttpGet]public async Task<IActionResult> Get(){var resultx11231 = await _defaultTableDbContext.Set<SysUserMod>().Where(o => o.Age == 198198).Select(o=>o.Id).ContainsAsync("1981");var resultx1121 = await _defaultTableDbContext.Set<SysUserMod>().Where(o => o.Id == "198").SumAsync(o=>o.Age);var resultx111 = await _defaultTableDbContext.Set<SysUserMod>().FirstOrDefaultAsync(o => o.Id == "198");var resultx2 = await _defaultTableDbContext.Set<SysUserMod>().CountAsync(o => o.Age<=10);var resultx = await _defaultTableDbContext.Set<SysUserMod>().Where(o => o.Id == "198").FirstOrDefaultAsync();var resultx33 = await _defaultTableDbContext.Set<SysUserMod>().Where(o => o.Id == "198").Select(o=>o.Id).FirstOrDefaultAsync();var resulxxt = await _defaultTableDbContext.Set<SysUserMod>().Where(o => o.Id == "198").ToListAsync();var result = await _defaultTableDbContext.Set<SysUserMod>().ToListAsync();var sysUserMod98 = result.FirstOrDefault(o => o.Id == "98");_defaultTableDbContext.Attach(sysUserMod98);sysUserMod98.Name = "name_update"+new Random().Next(1,99)+"_98";await _defaultTableDbContext.SaveChangesAsync();return Ok(result);}自定義分表鍵,自定義分表規(guī)則
目前市面上有的框架要么對(duì)分表字段有限制比如僅支持DateTime類型或者int等,要么對(duì)分表規(guī)則有限制:僅支持按天、按月、取模...等等,但是基于分表規(guī)則和分表字段是業(yè)務(wù)規(guī)則所以本框架遵循將其由業(yè)務(wù)系統(tǒng)自己定義,最大化來(lái)實(shí)現(xiàn)分表庫(kù)的適用性,基本上滿足一切分表規(guī)則,且sharding-core目前默認(rèn)提供一些常用的分表規(guī)則可以快速集成。
默認(rèn)路由
| AbstractSimpleShardingModKeyIntVirtualTableRoute | 取模 | 0,1,2... | = |
| AbstractSimpleShardingModKeyStringVirtualTableRoute | 取模 | 0,1,2... | = |
| AbstractSimpleShardingDayKeyDateTimeVirtualTableRoute | 按時(shí)間 | yyyyMMdd | >,>=,<,<=,=,contains |
| AbstractSimpleShardingDayKeyLongVirtualTableRoute | 按時(shí)間戳 | yyyyMMdd | >,>=,<,<=,=,contains |
| AbstractSimpleShardingWeekKeyDateTimeVirtualTableRoute | 按時(shí)間 | yyyyMMdd_dd | >,>=,<,<=,=,contains |
| AbstractSimpleShardingWeekKeyLongVirtualTableRoute | 按時(shí)間戳 | yyyyMMdd_dd | >,>=,<,<=,=,contains |
| AbstractSimpleShardingMonthKeyDateTimeVirtualTableRoute | 按時(shí)間 | yyyyMM | >,>=,<,<=,=,contains |
| AbstractSimpleShardingMonthKeyLongVirtualTableRoute | 按時(shí)間戳 | yyyyMM | >,>=,<,<=,=,contains |
| AbstractSimpleShardingYearKeyDateTimeVirtualTableRoute | 按時(shí)間 | yyyy | >,>=,<,<=,=,contains |
| AbstractSimpleShardingYearKeyLongVirtualTableRoute | 按時(shí)間戳 | yyyy | >,>=,<,<=,=,contains |
所謂的索引就是通過(guò)改對(duì)應(yīng)的條件操作符可以縮小減少指定表的范圍,加快程序的執(zhí)行
如果以上默認(rèn)分表無(wú)法滿足您的需求您還可以自定義分表,如何分表可以通過(guò)繼承 AbstractShardingOperatorVirtualTableRoute<TEntity,TKey>來(lái)實(shí)現(xiàn)自定義分表規(guī)則(近乎90%的規(guī)則都可以實(shí)現(xiàn))
動(dòng)態(tài)添加分表信息
很多分表組件默認(rèn)不帶動(dòng)態(tài)分表信息導(dǎo)致很多分表沒(méi)辦法根據(jù)業(yè)務(wù)系統(tǒng)來(lái)進(jìn)行動(dòng)態(tài)創(chuàng)建,sharding-core默認(rèn)提供動(dòng)態(tài)建表接口可以支持動(dòng)態(tài)按時(shí)間,按租戶等不需要數(shù)據(jù)做遷移的動(dòng)態(tài)分表信息,
如果需要請(qǐng)參考Samples.AutoByDate.SqlServer
支持select,join,group by等連表聚合函數(shù)
目前sharding-core支持select按需查詢,join分表連表查詢,group by聚合查詢,雖然本框架支持但是出于性能原因本框架還是不建議使用join操作符來(lái)操作,因?yàn)檫^(guò)多的表路由會(huì)導(dǎo)致笛卡爾積,會(huì)導(dǎo)致需要查詢的表集合增長(zhǎng)對(duì)數(shù)據(jù)庫(kù)連接比較考驗(yàn)。
以下代碼來(lái)自github的單元測(cè)試中,SysUserMod表示用戶表,SysUserSalary表示用戶月薪表用戶表按id取模,用戶月薪表按月分表
分頁(yè)
我們常說(shuō)的分頁(yè)是分表的難點(diǎn)也是最考驗(yàn)分表組件的
1我們首先來(lái)看普通的分表組件如何分頁(yè)
首先我們定義一組組數(shù)據(jù)比如是1-100的連續(xù)數(shù)字,然后分成兩張表按奇偶分表
| table1 | 1,3,5,7,9... |
| table2 | 2,4,6,8,10... |
這個(gè)情況是我們常見(jiàn)的也是最簡(jiǎn)單的分頁(yè),但是這個(gè)情況僅僅適用于數(shù)據(jù)量小的時(shí)候,如果用戶不小心點(diǎn)到了分頁(yè)的最后一頁(yè)那么結(jié)果將是災(zāi)難性的這是毋庸置疑的
那么sharding-core是如何處理的呢
無(wú)感知使用
目前的分表框架很少有做到無(wú)感知使用的,你在使用的時(shí)候好一點(diǎn)的框架不依賴三方,一般一點(diǎn)的不但要依賴很多三方框架并且在使用的時(shí)候還有一大堆限制,必須使用他的東西還沒(méi)辦法做到和dbcontext原生的使用方法。
sharding-core目前使用的是一種類似dbcontext的wrap模式,用一個(gè)新的dbcontext來(lái)包裝真實(shí)的dbcontext,這個(gè)包裝的dbcontext我們成為shardingdbcontext,shardingDbContext因?yàn)楸旧硪彩羌捎贒bContext所以它的使用方法和原生dbcontext沒(méi)有差別。并且僅需少量改動(dòng)即可支持abp和abp.next
讀寫(xiě)分離的支持
目前sharding-core已經(jīng)支持單node節(jié)點(diǎn)的讀寫(xiě)分離操作,將在不久的未來(lái)(1-2)天內(nèi)支持多節(jié)點(diǎn)的讀寫(xiě)分離
services.AddShardingDbContext<ShardingDefaultDbContext, DefaultDbContext>(o => o.UseSqlServer(hostBuilderContext.Configuration.GetSection("SqlServer")["ConnectionString"]),op =>{op.EnsureCreatedWithOutShardingTable = true;op.CreateShardingTableOnStart = true;op.UseShardingOptionsBuilder((connection, builder) => builder.UseSqlServer(connection).UseLoggerFactory(efLogger),(conStr,builder)=> builder.UseSqlServer("read db connection string").UseLoggerFactory(efLogger));op.AddShardingTableRoute<SysUserModVirtualTableRoute>();op.AddShardingTableRoute<SysUserSalaryVirtualTableRoute>();});未來(lái)計(jì)劃將支持分庫(kù),支持強(qiáng)制路由,顯示路由等...
最后具體如何使用且使用方式可以參考github(https://github.com/xuejmnet/sharding-core) 當(dāng)然我也會(huì)在后續(xù)出一系列的博客來(lái)對(duì)框架進(jìn)行支持的介紹
最后的最后
該文檔是我晚上趕工趕出來(lái)的也想趁熱打鐵希望更多的人關(guān)注,也希望更多的人可以交流。
憑借各大開(kāi)源生態(tài)圈提供的優(yōu)秀代碼和思路才有的這個(gè)框架,希望可以為.Net生態(tài)提供一份微薄之力,該框架本人會(huì)一直長(zhǎng)期維護(hù),有大神技術(shù)支持可以聯(lián)系下方方式歡迎star ????
博客
QQ群:771630778
個(gè)人QQ:326308290(歡迎技術(shù)支持提供您寶貴的意見(jiàn))
個(gè)人郵箱:326308290@qq.com
總結(jié)
以上是生活随笔為你收集整理的ShardingCore 如何呈现“完美”分表的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 聊一聊对外API接口的存活检查可以怎么做
- 下一篇: 最近看了两本低代码的书