efcore如何优雅的实现按年分库按月分表

news/2024/11/16 19:47:31/文章来源:https://www.cnblogs.com/xuejiaming/p/18198827

efcore如何优雅的实现按年分库按月分表

介绍

本文ShardinfCore版本
本期主角:

ShardingCore 一款ef-core下高性能、轻量级针对分表分库读写分离的解决方案,具有零依赖、零学习成本、零业务代码入侵适配

距离上次发文.net相关的已经有很久了,期间一直在从事java相关的工作,一不小心就卷了一个java的orm。easy-query 如果有.net相关小伙伴转java可以关注一下也算是打一波小广告。

这次发文主要是在期间有多名用户咨询分库分表相关的事宜,因为我之前并没有针对按年分库按月分表的demo实现,所以本次我打算借着这个机会对该框架进行一次讲解

说明

很多小伙伴我发现不会写GetRouteFilter这个方法不知道是什么意思
那么我们这边做一个很简单的案例


var tails = new List<string>();
tails.Add("202401");
tails.Add("202402");
tails.Add("202403");
tails.Add("202404");
DateTime shardingKey=new DateTime(2024,2,1);
var t = $"{shardingKey:yyyy.MM}";
Func<string, bool> filter = tail => tail.CompareTo(t) > 0;var list = tails.Where(filter).ToList();//如果上面的你会写那么下面的你会写吗,无非是上面全部是大于号而实际我们需要根据用户判断来确定应该返回什么public override Func<string, bool> GetRouteToFilter(DateTime shardingKey, ShardingOperatorEnum shardingOperator){var t = $"{shardingKey:yyyy.MM}";switch (shardingOperator){case ShardingOperatorEnum.GreaterThan:case ShardingOperatorEnum.GreaterThanOrEqual:return tail => String.Compare(tail, t, StringComparison.Ordinal) >= 0;case ShardingOperatorEnum.LessThan:{var currentMonth = ShardingCoreHelper.GetCurrentMonthFirstDay(shardingKey);//处于临界值 o=>o.time < [2021-01-01 00:00:00] 尾巴20210101不应该被返回if (currentMonth == shardingKey)return tail => String.Compare(tail, t, StringComparison.Ordinal) < 0;return tail => String.Compare(tail, t, StringComparison.Ordinal) <= 0;}case ShardingOperatorEnum.LessThanOrEqual:return tail => String.Compare(tail, t, StringComparison.Ordinal) <= 0;case ShardingOperatorEnum.Equal: return tail => tail == t;default:{return tail => true;}}}

步骤1

安装nuget

efcore架构

新建用户订单根据订单的创建时间年份进行分库月份进行分表

public class OrderItem
{/// <summary>/// 用户Id/// </summary>public string Id { get; set; }/// <summary>/// 购买用户/// </summary>public string User { get; set; }/// <summary>/// 付款金额/// </summary>public decimal PayAmount { get; set; }/// <summary>/// 创建时间/// </summary>public DateTime CreateTime { get; set; }
}
//数据库访问上下文
public class TestDbContext:AbstractShardingDbContext,IShardingTableDbContext
{public DbSet<OrderItem> OrderItems { get; set; }public TestDbContext(DbContextOptions<TestDbContext> options) : base(options){}public IRouteTail RouteTail { get; set; }protected override void OnModelCreating(ModelBuilder modelBuilder){base.OnModelCreating(modelBuilder);modelBuilder.Entity<OrderItem>().HasKey(o => o.Id);modelBuilder.Entity<OrderItem>().ToTable(nameof(OrderItem));}
}//分库路由
public class OrderItemDataSourceRoute:AbstractShardingOperatorVirtualDataSourceRoute<OrderItem,DateTime>
{private readonly ConcurrentBag<string> dataSources = new ConcurrentBag<string>();private readonly object _lock = new object();public override string ShardingKeyToDataSourceName(object shardingKey){return $"{shardingKey:yyyy}";//年份作为分库数据源名称}public override List<string> GetAllDataSourceNames(){return dataSources.ToList();}public override bool AddDataSourceName(string dataSourceName){var acquire = Monitor.TryEnter(_lock, TimeSpan.FromSeconds(3));if (!acquire){return false;}try{var contains = dataSources.Contains(dataSourceName);if (!contains){dataSources.Add(dataSourceName);return true;}}finally{Monitor.Exit(_lock);}return false;}public override void Configure(EntityMetadataDataSourceBuilder<OrderItem> builder){builder.ShardingProperty(o => o.CreateTime);}/// <summary>/// tail就是2020,2021,2022,2023 所以分片只需要格式化年就可以直接比较了/// </summary>/// <param name="shardingKey"></param>/// <param name="shardingOperator"></param>/// <returns></returns>public override Func<string, bool> GetRouteToFilter(DateTime shardingKey, ShardingOperatorEnum shardingOperator){var t = $"{shardingKey:yyyyy}";switch (shardingOperator){case ShardingOperatorEnum.GreaterThan:case ShardingOperatorEnum.GreaterThanOrEqual:return tail => String.Compare(tail, t, StringComparison.Ordinal) >= 0;case ShardingOperatorEnum.LessThan:{var currentYear =new DateTime(shardingKey.Year,1,1);//处于临界值 o=>o.time < [2021-01-01 00:00:00] 尾巴20210101不应该被返回if (currentYear == shardingKey)return tail => String.Compare(tail, t, StringComparison.Ordinal) < 0;return tail => String.Compare(tail, t, StringComparison.Ordinal) <= 0;}case ShardingOperatorEnum.LessThanOrEqual:return tail => String.Compare(tail, t, StringComparison.Ordinal) <= 0;case ShardingOperatorEnum.Equal: return tail => tail == t;default:{return tail => true;}}}
}//分表路由
public class OrderItemTableRoute:AbstractShardingOperatorVirtualTableRoute<OrderItem,DateTime>
{private readonly List<string> allTails = Enumerable.Range(1, 12).Select(o => o.ToString().PadLeft(2, '0')).ToList();public override string ShardingKeyToTail(object shardingKey){var time = Convert.ToDateTime(shardingKey);return $"{time:MM}";//01,02.....11,12}public override List<string> GetTails(){return allTails;}public override void Configure(EntityMetadataTableBuilder<OrderItem> builder){builder.ShardingProperty(o => o.CreateTime);}//注意这边必须将忽略数据源改成false
//注意这边必须将忽略数据源改成false
//注意这边必须将忽略数据源改成falseprotected override bool RouteIgnoreDataSource => false;//RouteIgnoreDataSource为false的时候那么tail就不是01,02......11,12了而是2021.01,2021.02.....会在tail里面带上数据源,就可以对齐进行筛选了
//如果你的数据源带了其他特殊标识请自行处理public override Func<string, bool> GetRouteToFilter(DateTime shardingKey, ShardingOperatorEnum shardingOperator){var t = $"{shardingKey:yyyyy.MM}";switch (shardingOperator){case ShardingOperatorEnum.GreaterThan:case ShardingOperatorEnum.GreaterThanOrEqual:return tail => String.Compare(tail, t, StringComparison.Ordinal) >= 0;case ShardingOperatorEnum.LessThan:{var currentMonth = ShardingCoreHelper.GetCurrentMonthFirstDay(shardingKey);//处于临界值 o=>o.time < [2021-01-01 00:00:00] 尾巴20210101不应该被返回if (currentMonth == shardingKey)return tail => String.Compare(tail, t, StringComparison.Ordinal) < 0;return tail => String.Compare(tail, t, StringComparison.Ordinal) <= 0;}case ShardingOperatorEnum.LessThanOrEqual:return tail => String.Compare(tail, t, StringComparison.Ordinal) <= 0;case ShardingOperatorEnum.Equal: return tail => tail == t;default:{return tail => true;}}}
}

startUp配置


ILoggerFactory efLogger = LoggerFactory.Create(builder =>
{builder.AddFilter((category, level) => category == DbLoggerCategory.Database.Command.Name && level == LogLevel.Debug).AddConsole();
});
builder.Services.AddShardingDbContext<TestDbContext>().UseRouteConfig(o =>{o.AddShardingDataSourceRoute<OrderItemDataSourceRoute>();o.AddShardingTableRoute<OrderItemTableRoute>();}).UseConfig((sp, o) =>{o.ThrowIfQueryRouteNotMatch = false;// var redisConfig = sp.GetService<RedisConfig>();// o.AddDefaultDataSource(redisConfig.Default, redisConfig.DefaultConn);// //redisConfig.ExtraConfigs// o.AddExtraDataSource();o.AddDefaultDataSource("2024", "server=127.0.0.1;port=3306;database=sd2024;userid=root;password=root;");o.UseShardingQuery((conn, b) =>{b.UseMySql(conn, new MySqlServerVersion(new Version())).UseLoggerFactory(efLogger);});o.UseShardingTransaction((conn, b) =>{b.UseMySql(conn, new MySqlServerVersion(new Version())).UseLoggerFactory(efLogger);});}).AddShardingCore();

startUp初始化

//初始化额外表var shardingRuntimeContext = app.Services.GetService<IShardingRuntimeContext<TestDbContext>>();
var dataSourceRouteManager = shardingRuntimeContext.GetDataSourceRouteManager();
var virtualDataSourceRoute = dataSourceRouteManager.GetRoute(typeof(OrderItem));
virtualDataSourceRoute.AddDataSourceName("2024");
virtualDataSourceRoute.AddDataSourceName("2023");
virtualDataSourceRoute.AddDataSourceName("2022");
DynamicShardingHelper.DynamicAppendDataSource(shardingRuntimeContext,"2023","server=127.0.0.1;port=3306;database=sd2023;userid=root;password=root;",false,false);
DynamicShardingHelper.DynamicAppendDataSource(shardingRuntimeContext,"2022","server=127.0.0.1;port=3306;database=sd2022;userid=root;password=root;",false,false);using (var scope = app.Services.CreateScope())
{var testDbContext = scope.ServiceProvider.GetService<TestDbContext>();testDbContext.Database.EnsureCreated();
}app.Services.UseAutoTryCompensateTable();

编写控制器

public async Task<IActionResult> Init(){var orderItems = new List<OrderItem>();var dateTime = new DateTime(2022,1,1);var end = new DateTime(2025,1,1);int i = 0;while (dateTime < end){orderItems.Add(new OrderItem(){Id = i.ToString(),User = "用户"+i.ToString(),PayAmount=i,CreateTime = dateTime,});i++;dateTime = dateTime.AddDays(15);}await _testDbContext.OrderItems.AddRangeAsync(orderItems);await _testDbContext.SaveChangesAsync();return Ok("hello world");}public async Task<IActionResult> Query([FromQuery]int current){var dateTime = new DateTime(2023,1,1);var shardingPagedResult = await _testDbContext.OrderItems.Where(o => o.CreateTime > dateTime).OrderBy(o=>o.CreateTime).ToShardingPageAsync(current, 20);return Ok(shardingPagedResult);}

初始化接口

查询

通过断点我们可以清晰地看到路由里面的2022年数据已经被彻底排除仅有2023和2024年的数据

后续

通过观察控制台我们看到了它打印了非常多的sql因为这边并没有对排序进行一个优化具体可以观看我的前几期文章内容做一个CreateEntityQueryConfiguration

分库路由和分表路由都需要进行编写CreateEntityQueryConfiguration

最后的最后

附上demo:ShardingYearDataBaseMonthTable https://github.com/xuejmnet/ShardingYearDataBaseMonthTable

您都看到这边了确定不点个star或者赞吗,一款.Net不得不学的分库分表解决方案,简单理解为分库分表技术在.net中的实现并且支持更多特性和更优秀的数据聚合,拥有原生性能的97%,并且无业务侵入性,支持未分片的所有efcore原生查询

  • github地址 https://github.com/xuejmnet/sharding-core
  • gitee地址 https://gitee.com/dotnetchina/sharding-core

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/709029.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[转帖]原理篇:Redis性能优化一定会涉及的9个项目(超全)!

https://heapdump.cn/monographic/detail/33/4338896 Redis 作为优秀的内存数据库,其拥有非常高的性能,单个实例的 OPS 能够达到 10W 左右。但也正因此如此,当我们在使用 Redis 时,如果发现操作延迟变大的情况,就会与我们的预期不符。 你也许或多或少地,也遇到过以下这些…

[转帖]CPU性能优化基本篇:一定要了解Linux CPU哪些基本概念

https://heapdump.cn/monographic/detail/20/4103469 目录 第一篇:CPU性能优化基础篇:一定要了解Linux CPU哪些基本概念 第二篇:CPU 优化高级篇:Linux系统中CPU占用率较高问题排查思路与解决方法第三篇:CPU 优化高级篇:Java CPU 高的原因和排查方法 :如何定位Java 消…

Nodejs的require流程

nodejs的require流程 . 一、require简单流程 nodejs的require流程大致如下:原图:https://images.cnblogs.com/cnblogs_com/blogs/668717/galleries/2013323/o_240518085412_require流程探索.png . 二、hook require的简单模板 function makeRequireFunction(_module_) {const…

2024-05-20 通达信选股 不破放量大阴底

AA:=HHVBARS(V,21); AB:=REF(C,AA)<REF(O,AA);AC:=REF(C,AA)/REF(C,AA+1)<0.9;AD:=C>REF(C,AA);AE:= C>1;AXG: AB AND AC AND AD AND AE;不破底,说明主力吸筹,就可以考虑区间的高抛低吸

OpenXR™Loader-设计和操作

OpenXR™Loader-设计和操作 介绍 本文档包含了解如何为OpenXR加载程序进行开发以及与之交互的必要信息。本文档的预期用途是作为详细的设计文档和学习一般OpenXR加载程序行为的工具。 如果本文档与OpenXR规范之间存在任何差异,则以该文档为准。 OpenXR™和OpenXR标志是Khronos…

Win10电脑怎么调整光标闪烁速度

1.按键盘上的win键,呼出开始菜单 2.在开始菜单中输“键盘”,单击控制面板中的键盘设置。3.在弹出的界面中,调整光标闪烁速度

04--JS04--进阶

JavaScript04:拓展进阶 一. jsonp 为了解决浏览器跨域问题,jQuery提供了jsonp请求 在网页端如果见到了服务器返回的数据是: ​ xxxxxxxxxxdjsfkldasjfkldasjklfjadsklfjasdlkj( { json数据 } ) ​ 在Preview里面可以像看到json一样去调试 这就是jsonp,依然是Ajax jsonp的逻辑…

.NET 中 Channel 类简单使用

Channel 是干什么的 The System.Threading.Channels namespace provides a set of synchronization data structures for passing data between producers and consumers asynchronously. The library targets .NET Standard and works on all .NET implementations. Channels …

第五周-云计算运维作业

1. 总结openssh服务安全加固和总结openssh免密认证原理,及免认证实现过程。 1.仅使用SSHv2 协议 2.关闭或者延迟压缩 Compression no 3.限制身份验证最大尝试次数 MaxAuthTries 3 4.禁用root账户登录 PermitRootLogin no 5.显示最后一次登录的日期和时间 PrintLastLog yes #用…

深入掌握service

深入掌握Service Service是Kubernetes实现微服务架构的核心概念,通过创建Service,可以为一组具有相同功能的容器应用提供一个统一的入口地址,并且将请求负载分发到后端的各个容器应用上。 Service定义 Service用于为一组提供服务的Pod抽象一个稳定的网络访问地址,是Kuberne…