C#中efcore-ShardingCore呈現(xiàn)“完美”分表
如果您對(duì)分表有以下痛點(diǎn)那么不妨試試我這邊開源的框架sharding-core ,是否需要無感知使用分表組件,是否需要支持abp,是否需要支持自定義分表規(guī)則,是否需要支持自定義分表鍵,是否需要支持特定的efcore版本,是否希望框架不帶任何三方框架干凈,是否需要支持讀寫分離,是否需要?jiǎng)討B(tài)添加表,是否需要支持join,group等操作,是否需要支持追蹤特性,是否想在不修改原先代碼的基礎(chǔ)上擴(kuò)展分表功能,如果一起上幾個(gè)條件任意組合且你在市面上沒辦法找到可替代的框架可以試試本框架。如何使用代碼具體可以參考github 將代碼下載下來如果本地裝了sqlserver直接運(yùn)行單元測試或者Sample.SqlServer程序會(huì)自動(dòng)在本地新建數(shù)據(jù)庫新建數(shù)據(jù)庫表結(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,有人要問為什么不支持6.x呢(小弟剛剛在上周完成對(duì)本框架的開發(fā)重構(gòu),目前還未對(duì)efcore 6.x進(jìn)行著手不過將在不遠(yuǎn)的將來即將支持(目測1-2個(gè)星期內(nèi)))。
目前efcore生態(tài)下有著許許多多的分表、分庫的解決方案,但是目前來講都有其不足點(diǎn),比如需要手動(dòng)設(shè)置分表后綴、需要大量替換現(xiàn)有代碼、不支持事務(wù)等等一系列問題,所以在這個(gè)大前提下我之前開源了sharding-core 分表組件,這個(gè)分表組件是目前來說個(gè)人認(rèn)為比較“完美”的分表組件,這個(gè)分表組件目前是參考了sharding-jdbc來實(shí)現(xiàn)的,但是比sharding-jdbc更加強(qiáng)大(因?yàn)镃#的表達(dá)式)。首先我們來看下目前市面上有的分表組件的缺點(diǎn)我們來針對(duì)其缺點(diǎn)進(jìn)行痛點(diǎn)解決。

efcore支持情況
| efcore版本 | 是否支持 |
|---|---|
| 2.x | 支持 |
| 3.x | 支持 |
| 5.x | 支持 |
| 6.x | 即將支持 |
數(shù)據(jù)庫支持情況
| 數(shù)據(jù)庫 | 理論是否支持 |
|---|---|
| SqlServer | 支持 |
| MySql | 支持 |
| PostgreSql | 支持 |
| SQLite | 支持 |
| Oracle | 支持 |
| 其他 | 支持(只要efcore支持) |
理論上只要是efcore對(duì)應(yīng)版本支持的數(shù)據(jù)庫,sharding-core都將支持。
如何開始使用
1.創(chuàng)建一個(gè)數(shù)據(jù)庫對(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)建dbcontext
op.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)自己定義,最大化來實(shí)現(xiàn)分表庫的適用性,基本上滿足一切分表規(guī)則,且sharding-core目前默認(rèn)提供一些常用的分表規(guī)則可以快速集成。
默認(rèn)路由
| 抽象abstract | 路由規(guī)則 | tail | 索引 |
|---|---|---|---|
| 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 |
所謂的索引就是通過改對(duì)應(yīng)的條件操作符可以縮小減少指定表的范圍,加快程序的執(zhí)行
如果以上默認(rèn)分表無法滿足您的需求您還可以自定義分表,如何分表可以通過繼承 AbstractShardingOperatorVirtualTableRoute<TEntity,TKey>來實(shí)現(xiàn)自定義分表規(guī)則(近乎90%的規(guī)則都可以實(shí)現(xiàn))
動(dòng)態(tài)添加分表信息
很多分表組件默認(rèn)不帶動(dòng)態(tài)分表信息導(dǎo)致很多分表沒辦法根據(jù)業(yè)務(wù)系統(tǒng)來進(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操作符來操作,因?yàn)檫^多的表路由會(huì)導(dǎo)致笛卡爾積,會(huì)導(dǎo)致需要查詢的表集合增長對(duì)數(shù)據(jù)庫連接比較考驗(yàn)。
以下代碼來自github的單元測試中,SysUserMod表示用戶表,SysUserSalary表示用戶月薪表用戶表按id取模,用戶月薪表按月分表
//join查詢
var list = await (from u in _virtualDbContext.Set<SysUserMod>()
join salary in _virtualDbContext.Set<SysUserSalary>()
on u.Id equals salary.UserId
select new
{
u.Id,
u.Age,
Salary = salary.Salary,
DateOfMonth = salary.DateOfMonth,
Name = u.Name
}).ToListAsync();
//group聚合查詢
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),
AvgSalaryDecimal = g.Average(o => o.SalaryDecimal),
MinSalary = g.Min(o => o.Salary),
MaxSalary = g.Max(o => o.Salary)
}).ToListAsync();
分頁
我們常說的分頁是分表的難點(diǎn)也是最考驗(yàn)分表組件的
1我們首先來看普通的分表組件如何分頁
首先我們定義一組組數(shù)據(jù)比如是1-100的連續(xù)數(shù)字,然后分成兩張表按奇偶分表
| 表名 | 數(shù)據(jù) |
|---|---|
| table1 | 1,3,5,7,9... |
| table2 | 2,4,6,8,10... |
select * from table limit 2,2理論上結(jié)果3,4
如果本次查詢會(huì)做落到table1 和table2那么會(huì)改寫成 2句sql
第一句 select * from table1 limit 4 ---> 1,3,5,7
第二句 select * from table2 limit 4 ---> 2,4,6,8
將8條數(shù)據(jù)放入內(nèi)存然后排序
1,2,3,4,5,6,7,8
獲取第3到4條數(shù)據(jù) 結(jié)果[3,4]
這個(gè)情況是我們常見的也是最簡單的分頁,但是這個(gè)情況僅僅適用于數(shù)據(jù)量小的時(shí)候,如果用戶不小心點(diǎn)到了分頁的最后一頁那么結(jié)果將是災(zāi)難性的這是毋庸置疑的
那么sharding-core是如何處理的呢
select * from table limit 2,2
首先還是一樣對(duì)數(shù)據(jù)庫語句進(jìn)行改性并且生成對(duì)應(yīng)的sql
第一句 select * from table1 limit 4
第二句 select * from table2 limit 4
因?yàn)閍do.net默認(rèn)DataReader是流式獲取,只要連接不關(guān)閉那么可以一直實(shí)現(xiàn)next獲取到內(nèi)存
創(chuàng)建一個(gè)優(yōu)先級(jí)隊(duì)列一個(gè)可以具有排序功能的隊(duì)列
因?yàn)镈ataReader的特性我們分別對(duì)sql1和sql2進(jìn)行一次next獲取到2個(gè)數(shù)組一個(gè)是[1,.....] A和數(shù)組[2......] B
獲取到兩個(gè)數(shù)組我們只知道頭部第一個(gè)對(duì)象因?yàn)闆]有進(jìn)行后續(xù)的next所以無法知曉剩下的數(shù)據(jù)但是有一點(diǎn)可以知道后面的數(shù)據(jù)都是按sql的指定順序的所以都不會(huì)比當(dāng)前頭大或者小
先將1和2放入優(yōu)先級(jí)隊(duì)列可以知道如果asc那么數(shù)組A放在隊(duì)列頭 數(shù)組B放在隊(duì)列尾部,然后對(duì)優(yōu)先級(jí)隊(duì)列進(jìn)行poll彈出,并且對(duì)A進(jìn)行next這個(gè)時(shí)候A變成了[3,....]再將A放入優(yōu)先級(jí)隊(duì)列
這時(shí)候優(yōu)先級(jí)隊(duì)列就是B在前A在后依次操作,然后對(duì)分頁的進(jìn)行過濾因?yàn)橐^2個(gè)對(duì)象所以只需要空?qǐng)?zhí)行2次那么指針就會(huì)指向A數(shù)組的3和B數(shù)組的4,剩下的只要獲取2個(gè)數(shù)據(jù)就可以了,
這樣做可以保證內(nèi)存最小化,然后分頁不會(huì)成為程序的災(zāi)難。
無感知使用
目前的分表框架很少有做到無感知使用的,你在使用的時(shí)候好一點(diǎn)的框架不依賴三方,一般一點(diǎn)的不但要依賴很多三方框架并且在使用的時(shí)候還有一大堆限制,必須使用他的東西還沒辦法做到和dbcontext原生的使用方法。
sharding-core目前使用的是一種類似dbcontext的wrap模式,用一個(gè)新的dbcontext來包裝真實(shí)的dbcontext,這個(gè)包裝的dbcontext我們成為shardingdbcontext,shardingDbContext因?yàn)楸旧硪彩羌捎贒bContext所以它的使用方法和原生dbcontext沒有差別。并且僅需少量改動(dòng)即可支持abp和abp.next
讀寫分離的支持
目前sharding-core已經(jīng)支持單node節(jié)點(diǎn)的讀寫分離操作,將在不久的未來(1-2)天內(nèi)支持多節(jié)點(diǎn)的讀寫分離
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>();
});
未來計(jì)劃將支持分庫,支持強(qiáng)制路由,顯示路由等...
最后具體如何使用且使用方式可以參考github(https://github.com/xuejmnet/sharding-core)
到此這篇關(guān)于efcore-ShardingCore呈現(xiàn)“完美”分表的文章就介紹到這了,更多相關(guān)ShardingCore“完美”分表內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
asp.net實(shí)現(xiàn)識(shí)別客戶端瀏覽器或操作系統(tǒng)
這里給大家匯總了使用asp.net實(shí)現(xiàn)識(shí)別客戶端瀏覽器或操作系統(tǒng)的方法和示例代碼,有需要的小伙伴可以參考下。2015-10-10
.NET Core類庫System.Reflection.DispatchProxy實(shí)現(xiàn)簡易Aop的方法
這篇文章主要給大家介紹了關(guān)于.NET Core類庫System.Reflection.DispatchProxy實(shí)現(xiàn)簡易Aop的相關(guān)資料,文中通過示例代碼結(jié)束的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-12-12
asp.net viewstate 回發(fā)機(jī)制
ASP.NET中,為了模擬Winform中的事件響應(yīng)機(jī)制,微軟的工程師真是煞費(fèi)苦心,發(fā)明了“回發(fā)”機(jī)制,使得編寫WEB頁面變得和Winform一樣簡單。2010-03-03
ASP.NET Core MVC學(xué)習(xí)教程之路由(Routing)
這篇文章主要給大家介紹了關(guān)于ASP.NET Core MVC學(xué)習(xí)教程之路由(Routing)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用ASP.NET Core MVC具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-07-07
ASP.NET?Core項(xiàng)目中調(diào)用WebService的方法
這篇文章介紹了ASP.NET?Core項(xiàng)目中調(diào)用WebService的方法,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-03-03
如何在前臺(tái)腳本通過json傳遞數(shù)據(jù)到后臺(tái)(使用微軟自帶的ajax)
這篇文章主要介紹了如何使用微軟自帶的ajax在前臺(tái)腳本通過json傳遞數(shù)據(jù)到后臺(tái)的實(shí)現(xiàn)方法2013-08-08
.net頁面訪問次數(shù)統(tǒng)計(jì)實(shí)現(xiàn)原理與代碼
網(wǎng)站訪問量統(tǒng)計(jì)、頁面訪問次數(shù)統(tǒng)計(jì),比較實(shí)用的一個(gè)功能,很多新手朋友都想實(shí)現(xiàn),本文處于此目的整理了一些,感興趣的朋友可以了解下2013-01-01

