使用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項目,項目運行成功界面顯示如下:
輸入多條內容并提交
然后可以看到數據庫中的數據是分表存儲

本文內容為作者月井石原創,轉載請注明出處~

浙公網安備 33010602011771號