请选择 进入手机版 | 继续访问电脑版

[ASP.NET] C#中efcore-ShardingCore呈现“完满 ”分表

[复制链接]
查看193 | 回复41 | 2021-9-15 05:17:19 | 显示全部楼层 |阅读模式
目次

假如 您对分表有以下痛点那么不妨试试我这边开源的框架sharding-core ,是否必要 无感知使用 分表组件,是否必要 支持abp,是否必要 支持自定义分表规则,是否必要 支持自定义分表键,是否必要 支持特定的efcore版本,是否盼望 框架不带任何三方框架干净,是否必要 支持读写分离,是否必要 动态添加表,是否必要 支持join,group等操作,是否必要 支持追踪特性,是否想在不修改原先代码的基础上扩展分表功能,假如 一起上几个条件恣意 组合且你在市面上没办法找到可替换 的框架可以试试本框架。怎样 使用 代码具体 可以参考github 将代码下载下来假如 本地装了sqlserver直接运行单元测试或者Sample.SqlServer程序会主动 在本地新建数据库新建数据库表布局 ,如今 初始化数据为用户信息和用户对应的月薪信息表,用户表以用户id取模,用户月薪表以月份分表。

起首 必要 相识 本框架的一个版本号不然将对您的使用 产生肯定 的分期,如今 框架分为3个版本分别是2.x,3.x,5.x3个版本,分别对应efcore 2.x efcore 3.x efcore 5.x,有人要问为什么不支持6.x呢(小弟刚刚在上周完成对本框架的开发 重构,如今 还未对efcore 6.x举行 动手 不过将在不远的将来即将支持(目测1-2个星期内))。

如今 efcore生态下有着许很多 多的分表、分库的办理 方案,但是如今 来讲都有其不足点,比如必要 手动设置分表后缀、必要 大量更换 现有代码、不支持变乱 等等一系列题目 ,以是 在这个大条件 下我之前开源了sharding-core 分表组件,这个分表组件是如今 来说个人以为 比较“完善 ”的分表组件,这个分表组件如今 是参考了sharding-jdbc来实现的,但是比sharding-jdbc更加强大 (由于 C#的表达式)。起首 我们来看下如今 市面上有的分表组件的缺点我们来针对其缺点举行 痛点办理 。

C#中efcore-ShardingCore呈现“完满


”分表

efcore支持环境

efcore版本 是否支持
2.x 支持
3.x 支持
5.x 支持
6.x 即将支持

数据库支持环境

数据库 理论是否支持
SqlServer 支持
MySql 支持
PostgreSql 支持
SQLite 支持
Oracle 支持
其他 支持(只要efcore支持)

理论上只要是efcore对应版本支持的数据库,sharding-core都将支持。

怎样 开始使用

1.创建一个数据库对象继承IShardingTable并且在对应的分表字段上举行 [ShardingTableKey]特性的标注

  1. /// <summary>
  2. /// 用户表
  3. /// </summary>
  4. public class SysUserMod : IShardingTable
  5. {
  6. /// <summary>
  7. /// 用户Id用于分表
  8. /// </summary>
  9. [ShardingTableKey(TailPrefix = "_")]
  10. public string Id { get; set; }
  11. /// <summary>
  12. /// 用户名称
  13. /// </summary>
  14. public string Name { get; set; }
  15. /// <summary>
  16. /// 用户姓名
  17. /// </summary>
  18. public int Age { get; set; }
  19. }
复制代码

2.创建对应的实体表对应设置 保举 fluent api

  1. public class SysTestMap:IEntityTypeConfiguration<SysTest>
  2. {
  3. public void Configure(EntityTypeBuilder<SysTest> builder)
  4. {
  5. builder.HasKey(o => o.Id);
  6. builder.Property(o => o.Id).IsRequired().HasMaxLength(128);
  7. builder.Property(o => o.UserId).IsRequired().HasMaxLength(128);
  8. builder.ToTable(nameof(SysTest));
  9. }
  10. }
复制代码

3.创建对应的分表规则 取模分表,参数2代表后缀2位就是00-99最多100张表,3表示模3== key.hashcode() %3

  1. public class SysUserModVirtualTableRoute : AbstractSimpleShardingModKeyStringVirtualTableRoute<SysUserMod>
  2. {
  3. public SysUserModVirtualTableRoute() : base(2,3)
  4. {
  5. }
  6. }
复制代码

4.创建对应实行 的dbcontext 这一步除了继承IShardingTableDbContext外其他和平凡 dbcontext一样

  1. public class DefaultTableDbContext: DbContext,IShardingTableDbContext
  2. {
  3. public DefaultTableDbContext(DbContextOptions<DefaultTableDbContext> options) :base(options)
  4. {
  5. }
  6. protected override void OnModelCreating(ModelBuilder modelBuilder)
  7. {
  8. base.OnModelCreating(modelBuilder);
  9. modelBuilder.ApplyConfiguration(new SysUserModMap());
  10. }
  11. public IRouteTail RouteTail { get; set; }
  12. }
复制代码

5.添加分表dbcontext

  1. public class DefaultShardingDbContext:AbstractShardingDbContext<DefaultTableDbContext>
  2. {
  3. public DefaultShardingDbContext(DbContextOptions<DefaultShardingDbContext> options) : base(options)
  4. {
  5. }
  6. protected override void OnModelCreating(ModelBuilder modelBuilder)
  7. {
  8. base.OnModelCreating(modelBuilder);
  9. modelBuilder.ApplyConfiguration(new SysUserModMap());
  10. }
  11. public override Type ShardingDbContextType => this.GetType();
  12. }
复制代码

6.添加设置

  1. public void ConfigureServices(IServiceCollection services)
  2. {
  3. services.AddControllers();
  4. //原先的dbcontext可以用也可以不用如果原先的dbcontext还在用就继续
  5. //services.AddDbContext<DefaultTableDbContext>(o => o.UseSqlServer("Data Source=localhost;Initial Catalog=ShardingCoreDBxx3;Integrated Security=True"));
  6. services.AddShardingDbContext<DefaultShardingDbContext, DefaultTableDbContext>(
  7. o => o.UseSqlServer("Data Source=localhost;Initial Catalog=ShardingCoreDBxx2;Integrated Security=True;")
  8. , op =>
  9. {
  10. op.EnsureCreatedWithOutShardingTable = true;
  11. op.CreateShardingTableOnStart = true;
  12. op.UseShardingOptionsBuilder(
  13. (connection, builder) => builder.UseSqlServer(connection).UseLoggerFactory(efLogger),//使用dbconnection创建dbcontext支持事务
  14. (conStr,builder) => builder.UseSqlServer(conStr).UseLoggerFactory(efLogger));//使用链接字符串创建dbcontext
  15. op.AddShardingTableRoute<SysUserModVirtualTableRoute>();
  16. });
  17. }
  18. public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
  19. {
  20. ...
  21. //添加启动项
  22. app.UseShardingCore();
  23. ...
  24. }
  25. public static class ShardingCoreExtension{
  26. public static IApplicationBuilder UseShardingCore(this IApplicationBuilder app)
  27. {
  28. var shardingBootstrapper = app.ApplicationServices.GetRequiredService<IShardingBootstrapper>();
  29. shardingBootstrapper.Start();
  30. return app;
  31. }
  32. }
复制代码

7.控制器使用

  1. private readonly DefaultShardingDbContext _defaultTableDbContext;
  2. public ValuesController(DefaultShardingDbContext defaultTableDbContext)
  3. {
  4. _defaultTableDbContext = defaultTableDbContext;
  5. }
  6. [HttpGet]
  7. public async Task<IActionResult> Get()
  8. {
  9. var resultx11231 = await _defaultTableDbContext.Set<SysUserMod>().Where(o => o.Age == 198198).Select(o=>o.Id).ContainsAsync("1981");
  10. var resultx1121 = await _defaultTableDbContext.Set<SysUserMod>().Where(o => o.Id == "198").SumAsync(o=>o.Age);
  11. var resultx111 = await _defaultTableDbContext.Set<SysUserMod>().FirstOrDefaultAsync(o => o.Id == "198");
  12. var resultx2 = await _defaultTableDbContext.Set<SysUserMod>().CountAsync(o => o.Age<=10);
  13. var resultx = await _defaultTableDbContext.Set<SysUserMod>().Where(o => o.Id == "198").FirstOrDefaultAsync();
  14. var resultx33 = await _defaultTableDbContext.Set<SysUserMod>().Where(o => o.Id == "198").Select(o=>o.Id).FirstOrDefaultAsync();
  15. var resulxxt = await _defaultTableDbContext.Set<SysUserMod>().Where(o => o.Id == "198").ToListAsync();
  16. var result = await _defaultTableDbContext.Set<SysUserMod>().ToListAsync();
  17. var sysUserMod98 = result.FirstOrDefault(o => o.Id == "98");
  18. _defaultTableDbContext.Attach(sysUserMod98);
  19. sysUserMod98.Name = "name_update"+new Random().Next(1,99)+"_98";
  20. await _defaultTableDbContext.SaveChangesAsync();
  21. return Ok(result);
  22. }
复制代码

自定义分表键,自定义分表规则

如今 市面上有的框架要么对分表字段有限定 比如仅支持DateTime范例 或者int等,要么对分表规则有限定 :仅支持按天、按月、取模...等等,但是基于分表规则和分表字段是业务规则以是 本框架依照 将其由业务体系 本身 定义,最大化来实现分表库的实用 性,基本上满足 齐备 分表规则,且sharding-core如今 默认提供一些常用的分表规则可以快速集成。

默认路由

抽象abstract 路由规则 tail 索引
AbstractSimpleShardingModKeyIntVirtualTableRoute 取模 0,1,2... =
AbstractSimpleShardingModKeyStringVirtualTableRoute 取模 0,1,2... =
AbstractSimpleShardingDayKeyDateTimeVirtualTableRoute 按时间 yyyyMMdd >,>=,<,<=,=,contains
AbstractSimpleShardingDayKeyLongVirtualTableRoute 按时间戳 yyyyMMdd >,>=,<,<=,=,contains
AbstractSimpleShardingWeekKeyDateTimeVirtualTableRoute 按时间 yyyyMMdd_dd >,>=,<,<=,=,contains
AbstractSimpleShardingWeekKeyLongVirtualTableRoute 按时间戳 yyyyMMdd_dd >,>=,<,<=,=,contains
AbstractSimpleShardingMonthKeyDateTimeVirtualTableRoute 按时间 yyyyMM >,>=,<,<=,=,contains
AbstractSimpleShardingMonthKeyLongVirtualTableRoute 按时间戳 yyyyMM >,>=,<,<=,=,contains
AbstractSimpleShardingYearKeyDateTimeVirtualTableRoute 按时间 yyyy >,>=,<,<=,=,contains
AbstractSimpleShardingYearKeyLongVirtualTableRoute 按时间戳 yyyy >,>=,<,<=,=,contains

所谓的索引就是通过改对应的条件操作符可以缩小减少指定表的范围,加快程序的实行
假如 以上默认分表无法满足 您的需求您还可以自定义分表,怎样 分表可以通过继承 AbstractShardingOperatorVirtualTableRoute来实现自定义分表规则(近乎90%的规则都可以实现)

动态添加分表信息

很多分表组件默认不带动态分表信息导致很多分表没办法根据业务体系 来举行 动态创建,sharding-core默认提供动态建表接口可以支持动态按时间,按租户等不必要 数据做迁徙 的动态分表信息,
假如 必要 请参考Samples.AutoByDate.SqlServer

支持select,join,group by等连表聚合函数

如今 sharding-core支持select按需查询,join分表连表查询,group by聚合查询,固然 本框架支持但是出于性能缘故原由 本框架还是不建议使用 join操作符来操作,由于 过多的表路由会导致笛卡尔积,会导致必要 查询的表集合增长对数据库毗连 比较磨练 。
以下代码来自github的单元测试中,SysUserMod表示用户表,SysUserSalary表示用户月薪表用户表按id取模,用户月薪表按月分表

  1. //join查询
  2. var list = await (from u in _virtualDbContext.Set<SysUserMod>()
  3. join salary in _virtualDbContext.Set<SysUserSalary>()
  4. on u.Id equals salary.UserId
  5. select new
  6. {
  7. u.Id,
  8. u.Age,
  9. Salary = salary.Salary,
  10. DateOfMonth = salary.DateOfMonth,
  11. Name = u.Name
  12. }).ToListAsync();
  13. //group聚合查询
  14. var ids = new[] {"200", "300"};
  15. var dateOfMonths = new[] {202111, 202110};
  16. var group = await (from u in _virtualDbContext.Set<SysUserSalary>()
  17. .Where(o => ids.Contains(o.UserId) && dateOfMonths.Contains(o.DateOfMonth))
  18. group u by new
  19. {
  20. UId = u.UserId
  21. }
  22. into g
  23. select new
  24. {
  25. GroupUserId = g.Key.UId,
  26. Count = g.Count(),
  27. TotalSalary = g.Sum(o => o.Salary),
  28. AvgSalary = g.Average(o => o.Salary),
  29. AvgSalaryDecimal = g.Average(o => o.SalaryDecimal),
  30. MinSalary = g.Min(o => o.Salary),
  31. MaxSalary = g.Max(o => o.Salary)
  32. }).ToListAsync();
复制代码

分页

我们常说的分页是分表的难点也是最磨练 分表组件的
1我们起首 来看平凡 的分表组件怎样 分页
起首 我们定义一组组数据比如是1-100的连续 数字,然后分成两张表按奇偶分表

表名 数据
table1 1,3,5,7,9...
table2 2,4,6,8,10...
  1. select * from table limit 2,2理论上结果3,4
  2. 如果本次查询会做落到table1 和table2那么会改写成 2句sql
  3. 第一句 select * from table1 limit 4 ---> 1,3,5,7
  4. 第二句 select * from table2 limit 4 ---> 2,4,6,8
  5. 将8条数据放入内存然后排序
  6. 1,2,3,4,5,6,7,8
  7. 获取第3到4条数据 结果[3,4]
复制代码

这个环境 是我们常见的也是最简单的分页,但是这个环境 仅仅实用 于数据量小的时间 ,假如 用户不鉴戒 点到了分页的末了 一页那么结果 将是灾难 性的这是毋庸置疑的
那么sharding-core是怎样 处理的呢

  1. select * from table limit 2,2
  2. 首先还是一样对数据库语句进行改性并且生成对应的sql
  3. 第一句 select * from table1 limit 4
  4. 第二句 select * from table2 limit 4
  5. 因为ado.net默认DataReader是流式获取,只要连接不关闭那么可以一直实现next获取到内存
  6. 创建一个优先级队列一个可以具有排序功能的队列
  7. 因为DataReader的特性我们分别对sql1和sql2进行一次next获取到2个数组一个是[1,.....] A和数组[2......] B
  8. 获取到两个数组我们只知道头部第一个对象因为没有进行后续的next所以无法知晓剩下的数据但是有一点可以知道后面的数据都是按sql的指定顺序的所以都不会比当前头大或者小
  9. 先将1和2放入优先级队列可以知道如果asc那么数组A放在队列头 数组B放在队列尾部,然后对优先级队列进行poll弹出,并且对A进行next这个时候A变成了[3,....]再将A放入优先级队列
  10. 这时候优先级队列就是B在前A在后依次操作,然后对分页的进行过滤因为要跳过2个对象所以只需要空执行2次那么指针就会指向A数组的3和B数组的4,剩下的只要获取2个数据就可以了,
  11. 这样做可以保证内存最小化,然后分页不会成为程序的灾难。
复制代码

无感知使用

如今 的分表框架很少有做到无感知使用 的,你在使用 的时间 好一点的框架不依靠 三方,一样平常 一点的不但要依靠 很多三方框架并且在使用 的时间 还有一大堆限定 ,必须使用 他的东西还没办法做到和dbcontext原生的使用 方法。
sharding-core如今 使用 的是一种类似 dbcontext的wrap模式,用一个新的dbcontext来包装真实的dbcontext,这个包装的dbcontext我们成为shardingdbcontext,shardingDbContext由于 本身也是集成于DbContext以是 它的使用 方法和原生dbcontext没有差别。并且仅需少量改动即可支持abp和abp.next

读写分离的支持

如今 sharding-core已经支持单node节点的读写分离操作,将在不久的将来 (1-2)天内支持多节点的读写分离

  1. services.AddShardingDbContext<ShardingDefaultDbContext, DefaultDbContext>(o => o.UseSqlServer(hostBuilderContext.Configuration.GetSection("SqlServer")["ConnectionString"])
  2. ,op =>
  3. {
  4. op.EnsureCreatedWithOutShardingTable = true;
  5. op.CreateShardingTableOnStart = true;
  6. op.UseShardingOptionsBuilder((connection, builder) => builder.UseSqlServer(connection).UseLoggerFactory(efLogger),
  7. (conStr,builder)=> builder.UseSqlServer("read db connection string").UseLoggerFactory(efLogger));
  8. op.AddShardingTableRoute<SysUserModVirtualTableRoute>();
  9. op.AddShardingTableRoute<SysUserSalaryVirtualTableRoute>();
  10. });
复制代码

将来 计划将支持分库,支持逼迫 路由,表现 路由等...

末了 具体 怎样 使用 且使用 方式可以参考github(https://github.com/xuejmnet/sharding-core)

到此这篇关于efcore-ShardingCore呈现“完善 ”分表的文章就先容 到这了,更多干系 ShardingCore“完善 ”分表内容请搜索 脚本之家从前 的文章或继续欣赏 下面的干系 文章盼望 大家以后多多支持脚本之家!


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

avatar 嫣冉 | 2021-9-15 12:00:29 | 显示全部楼层
看了这么多帖子,第一次看看到这么有内涵的!
回复

使用道具 举报

avatar 123457710 | 2021-9-21 20:58:08 | 显示全部楼层
十分赞同admin楼主!
回复

使用道具 举报

avatar 同感丶 | 2021-9-22 14:28:10 | 显示全部楼层
学习雷锋,好好回帖!
回复

使用道具 举报

avatar 醉于山水 | 2021-9-23 06:38:26 | 显示全部楼层
吹牛的人越来越多了!
回复

使用道具 举报

avatar 大头226 | 2021-9-26 15:53:55 | 显示全部楼层
admin楼主,您忘记吃药了吧?
回复

使用道具 举报

avatar 123457064 | 2021-10-3 11:58:11 | 显示全部楼层
收藏了,怕admin楼主删了!
回复

使用道具 举报

avatar 山风点烟捶 | 2021-10-3 20:40:03 | 显示全部楼层
论坛的人气越来越旺了!
回复

使用道具 举报

avatar 彭832 | 2021-10-3 21:29:45 | 显示全部楼层
宇宙第一贴诞生了!
回复

使用道具 举报

avatar 123457049 | 2021-10-3 21:38:37 | 显示全部楼层
楼上的能详细介绍一下么?
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则