使用ShardingCore分表
项目环境:ABP+Mysql
一、准备一个ABP项目
这里采用ABP的Samples中的一个样板项目TodoApp下的Mvc-EfCore
下载地址:https://github.com/abpframework/abp-samples/tree/master/TodoApp/Mvc-EfCore
二、项目数据层切换为Mysql
ABP样板项目大多连接的是SqlServer,在这里我们需要先将数据源切换到Mysql,具体如下:
1.将TodoApp.Web和TodoApp.DbMigrator项目下的appsettings.json文件里的默认数据库连接字符串替换为Mysql数据库连接字符串。
2.调整TodoApp.EntityFrameworkCore项目下的DbContext数据源
3.调整TodoApp.EntityFrameworkCore项目下的DbContextFactory数据源
4.调整TodoApp.EntityFrameworkCore项目下EntityFrameworkCore启动模块TodoAppEntityFrameworkCoreModule的数据源
三、集成ShardingCore分表功能
1.在EntityFrameworkCore项目下添加ShardingCore依赖包,目前最新版是6.6.0.27。
2.在TodoApp.Domain.Shard项目下新建两个接口IShardingKeyIsGuId,IShardingKeyIsCreationTime
用于将创建时间或Guid指定为ShardingKey,因为ShardingCore需要add,remove,update的时候shardingkey不可以为空。
namespace TodoApp { public interface IShardingKeyIsGuId { } }
namespace TodoApp { public interface IShardingKeyIsCreationTime { } }
3.创建AbpDbContext抽象类
- abp:只要你的dbcontext继承至
public class TDbContext:AbpDbContext<TDbContext>
那么就可以完美使用. 但是sharding-core的使用我们通过readme来查看发现
- sharidng-core:只要你的dbcontext继承至
public class TDbContext:AbstractShardingDbContext
那么你就可以完美使用.
但c#又不是c++没有多继承那怎么办,这边其实是有一个误区的就是abp确实需要继承abpdbcontext但是sharding-core是以接口作为依赖来开发的,所以我们只需要实现
ISharingDbContext
这个接口就可以,如果需要事务在实现ISupportShardingTransaction
最终我们是通过实现一个抽象基类来继承abpdbcntext并且实现sharding-core需要的接口 AbstractShardingAbpDbContext 这样我们就可以在不破坏abp的同时又兼顾了sharding-core注意:这边sharing-core让继承AbstractShardingDbContext是因为重复写这些接口的实现会很麻烦所以写了一个抽象类方便使用
集成ShardingCore的AbpDbContext抽象类源码:https://github.com/dotnetcore/sharding-core/blob/main/samples/Samples.AbpSharding/AbstractShardingAbpDbContext.cs
直接在TodoApp.EntityFrameworkCore项目的EntityFrameworkCore下新建一个AbstractShardingAbpDbContext抽象类,并将源码里的内容全部写进来。
由于这一块代码太多,为避免占用过多篇幅,就只放其中一部分截图
4.配置DbContext类
注释掉原本继承的AbpDbContext类,改为继承AbstractShardingAbpDbContext抽象类和IShardingTableDbContext接口
5.配置表对象实体类
6.新建分表路由
由于目前的场景是只分表不分库,所以这里只需要创建分表路由,设定对TodoItem按照ID分表
using ShardingCore.VirtualRoutes.Mods; using ShardingCore.Core.EntityMetadatas; namespace TodoApp.EntityFrameworkCore { public class TodoTableRoute:AbstractSimpleShardingModKeyStringVirtualTableRoute<TodoItem> { public TodoTableRoute() : base(2, 3) { } public override void Configure(EntityMetadataTableBuilder<TodoItem> builder) { builder.ShardingProperty(o => o.Id); } } }
这里指定了对应的分表规则 根据ID取模分表,参数2代表后缀2位就是00-99最多100张表,3表示模3== key.hashcode() %3
默认路由
抽象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<TEntity,TKey>来实现自定义分表规则(近乎90%的规则都可以实现)
7.新建迁移数据库脚本生成类
using System.Diagnostics.CodeAnalysis; using System.Linq; using ShardingCore.Helpers; using ShardingCore.Core.RuntimeContexts; using Pomelo.EntityFrameworkCore.MySql.Migrations; using Pomelo.EntityFrameworkCore.MySql.Infrastructure.Internal; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations.Operations; namespace TodoApp.EntityFrameworkCore { public class ShardingMySqlMigrationsSqlGenerator:MySqlMigrationsSqlGenerator { private readonly IShardingRuntimeContext _shardingRuntimeContext; public ShardingMySqlMigrationsSqlGenerator(IShardingRuntimeContext shardingRuntimeContext, [NotNull] MigrationsSqlGeneratorDependencies dependencies, [NotNull] IRelationalAnnotationProvider migrationsAnnotations, [NotNull] IMySqlOptions options) : base(dependencies, migrationsAnnotations,options) { _shardingRuntimeContext = shardingRuntimeContext; } protected override void Generate( MigrationOperation operation, IModel model, MigrationCommandListBuilder builder) { var oldCmds = builder.GetCommandList().ToList(); base.Generate(operation, model, builder); var newCmds = builder.GetCommandList().ToList(); var addCmds = newCmds.Where(x => !oldCmds.Contains(x)).ToList(); MigrationHelper.Generate(_shardingRuntimeContext,operation, builder, Dependencies.SqlGenerationHelper, addCmds); } } }
8.配置EntityFrameworkCore启动模块ShardingCore的依赖注入
public class TodoAppEntityFrameworkCoreModule : AbpModule { public override void PreConfigureServices(ServiceConfigurationContext context) { TodoAppEfCoreEntityExtensionMappings.Configure(); } public override void ConfigureServices(ServiceConfigurationContext context) { context.Services.AddAbpDbContext<TodoAppDbContext>(options => { /* Remove "includeAllEntities: true" to create * default repositories only for aggregate roots */ options.AddDefaultRepositories(includeAllEntities: true); }); var configuration = context.Services.GetConfiguration(); string mysqlconn = configuration["ConnectionStrings:Default"]; Configure<AbpDbContextOptions>(options => { /* The main point to change your DBMS. * See also TodoAppDbContextFactory for EF Core tooling. */ options.UseMySQL(); // options.UseSqlServer(); options.Configure<TodoAppDbContext>(innerContext => { ShardingCoreExtension.UseDefaultSharding<TodoAppDbContext>(innerContext.ServiceProvider, innerContext.DbContextOptions); }); }); context.Services.AddShardingDbContext<TodoAppDbContext>() .UseRouteConfig(op => { op.AddShardingTableRoute<TodoTableRoute>(); }) .UseConfig((sp, op) => { op.ThrowIfQueryRouteNotMatch = false; op.UseShardingQuery((conStr, builder) => { builder.UseMySql(conStr, new MySqlServerVersion(new Version())); }); op.UseShardingTransaction((connection, builder) => { builder.UseMySql(connection, new MySqlServerVersion(new Version())); }); op.UseShardingMigrationConfigure(builder => { builder.ReplaceService<IMigrationsSqlGenerator, ShardingMySqlMigrationsSqlGenerator>(); }); op.AddDefaultDataSource("ds0",mysqlconn); // op.AddExtraDataSource(sp => // { // return new Dictionary<string, string>() // { // {"ds1", ""}, // {"ds2", ""} // }; // }); }).ReplaceService<ITableEnsureManager,MySqlTableEnsureManager>() .AddShardingCore(); } public override void OnPostApplicationInitialization(ApplicationInitializationContext context) { base.OnPostApplicationInitialization(context); //创建表的定时任务如果有按年月日系统默认路由的需要系统创建的记得开起来 context.ServiceProvider.UseAutoShardingCreate(); // context.ServiceProvider.UseAutoTryCompensateTable(); //补偿表 //自动迁移的话不需要 } }
至此我们就完成了集成ShardingCore的基本配置。
四、数据迁移
1.清理迁移文件夹
执行数据迁移前,记得先将之前迁移文件夹下生成的同名迁移类清除掉,避免冲突,尤其是此样板项目中默认自带的是SqlServer数据库的迁移类,都需要先进行清理掉。
2.生成数据迁移类
打开Rider控制台终端,输入创建迁移类命令
dotnet ef migrations add Inital -s ./src/TodoApp.Web/TodoApp.Web.csproj -c TodoAppDbContext -p ./src/TodoApp.EntityFrameworkCore/TodoApp.EntityFrameworkCore.csproj
回车,等待迁移类生成,生成成功后可以在Migrations文件夹下看到两个新生成的迁移类
3.执行迁移
右键运行TodoApp.DbMigrator项目,它会自动执行迁移类进行数据迁移
迁移成功后,可以在Mysql数据库中看到新生成的TodoApp数据库和数据表,其中TodoItems表被分成了3张。
至此,数据迁移成功。
五、运行项目测试分表功能
右键运行TodoApp.Web项目,项目运行成功界面显示如下:
输入多条内容并提交
然后可以看到数据库中的数据是分表存储
本文内容为作者月井石原创,转载请注明出处~