簡介
本框架旨在為EF Core提供Sharding(即讀寫分離分庫分表)支援,不僅提供了一套強大的普通資料操作介面,並且降低了分表難度,支援按時間自動分表擴容,提供的操作介面簡潔統一.
原始碼地址:EFCore.SHarding
引言
讀寫分離分庫分表一直是資料庫領域中的重難點,當資料規模達到單庫極限的時候,就不得不考慮分表方案。EF Core作為.NET Core中最為主流的ORM,用起來十分方便快捷,但是官方並沒有相應的Sharding支援,鄙人不才,經過一番摸索之後終於完成這個框架.
開始
準備
首先根據需要安裝對應的Nuget包
包名 | 說明 |
---|---|
EFCore.Sharding | 必裝包,3.x版本對應EF Core3.x,2.x版本對應EF Core2.x |
EFCore.Sharding.MySql | MySql支援 |
EFCore.Sharding.PostgreSql | PostgreSql支援 |
EFCore.Sharding.SQLite | SQLite支援 |
EFCore.Sharding.SqlServer | SqlServer支援 |
EFCore.Sharding.Oracle | Oracle支援(暫不支援3.x) |
配置
class Base_UnitTestShardingRule : ModShardingRule<Base_UnitTest>
{
protected override string KeyField => "Id";
protected override int Mod => 3;
}
ShardingConfig.Init(config =>
{
config.AddAbsDb(DatabaseType.SQLite)
.AddPhysicDb(ReadWriteType.Read | ReadWriteType.Write, "DataSource=db.db")
.AddPhysicDbGroup()
.AddPhysicTable<Base_UnitTest>("Base_UnitTest_0")
.AddPhysicTable<Base_UnitTest>("Base_UnitTest_1")
.AddPhysicTable<Base_UnitTest>("Base_UnitTest_2")
.SetShardingRule(new Base_UnitTestShardingRule());
});
上述程式碼中完成了Sharding配置
- AddAbsDb是指新增抽象資料庫,抽象資料庫就是將多個分庫看成同一個資料庫來進行操作
- AddPhysicDbGroup是指新增物理資料庫組,在同一組物理資料庫中,它們資料庫型別相同,擁有的表相同,每個資料庫擁有的資料是一致的(之間通過主主複製或主從複製進行資料同步)
- AddPhysicTable是指新增物理資料表,傳入的Base_UnitTest是抽象資料表(即將Base_UnitTest拆分為Base_UnitTest_0~2)
- Base_UnitTestShardingRule是採用的分表規則,上述程式碼中採用的是雜湊取模的分表方式
使用
配置完成,下面開始使用,使用方式非常簡單,與平常使用基本一致
首先獲取分片倉儲介面IShardingRepository
IShardingRepository _db = DbFactory.GetRepository().ToSharding();
然後即可進行資料操作:
Base_UnitTest _newData = new Base_UnitTest
{
Id = Guid.NewGuid().ToString(),
UserId = "Admin",
UserName = "超級管理員",
Age = 22
};
List<Base_UnitTest> _insertList = new List<Base_UnitTest>
{
new Base_UnitTest
{
Id = Guid.NewGuid().ToString(),
UserId = "Admin1",
UserName = "超級管理員1",
Age = 22
},
new Base_UnitTest
{
Id = Guid.NewGuid().ToString(),
UserId = "Admin2",
UserName = "超級管理員2",
Age = 22
}
};
//新增單條資料
_db.Insert(_newData);
//新增多條資料
_db.Insert(_insertList);
//清空表
_db.DeleteAll<Base_UnitTest>();
//刪除單條資料
_db.Delete(_newData);
//刪除多條資料
_db.Delete(_insertList);
//刪除指定資料
_db.Delete<Base_UnitTest>(x => x.UserId == "Admin2");
//更新單條資料
_db.Update(_newData);
//更新多條資料
_db.Update(_insertList);
//更新單條資料指定屬性
_db.UpdateAny(_newData, new List<string> { "UserName", "Age" });
//更新多條資料指定屬性
_db.UpdateAny(_insertList, new List<string> { "UserName", "Age" });
//更新指定條件資料
_db.UpdateWhere<Base_UnitTest>(x => x.UserId == "Admin", x =>
{
x.UserId = "Admin2";
});
//GetList獲取表的所有資料
var list=_db.GetList<Base_UnitTest>();
//GetIQPagination獲取分頁後的資料
var list=_db.GetIShardingQueryable<Base_UnitTest>().GetPagination(pagination);
//Max
var max=_db.GetIShardingQueryable<Base_UnitTest>().Max(x => x.Age);
//Min
var min=_db.GetIShardingQueryable<Base_UnitTest>().Min(x => x.Age);
//Average
var min=_db.GetIShardingQueryable<Base_UnitTest>().Average(x => x.Age);
//Count
var min=_db.GetIShardingQueryable<Base_UnitTest>().Count();
//事務,使用方式與普通事務一致
bool succcess = _db.RunTransaction(() =>
{
_db.Insert(_newData);
var newData2 = _newData.DeepClone();
_db.Insert(newData2);
}).Success;
Assert.AreEqual(succcess, false);
上述操作中表面上是操作Base_UnitTest表,實際上卻在按照一定規則使用Base_UnitTest_0~2三張表,使分片對業務操作透明,極大提高開發效率
具體使用方式請參考單元測試原始碼:連線
按時間自動分表
上面的雜湊取模的方式雖然簡單,但是卻十分不實用,因為當3張分表到達瓶頸時,將會面臨擴容的問題,這種方式擴容需要進行大量的資料遷移,這無疑是十分麻煩的。因此需要一種方式能夠系統自動建表擴容,並且無需人工干預,這就是按時間自動分表.
using Demo.Common;
using EFCore.Sharding;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace Demo.AutoExpandByDate
{
class Base_UnitTestShardingRule : AbsShardingRule<Base_UnitTest>
{
public override DateTime BuildDate(Base_UnitTest obj)
{
return obj.CreateTime;
}
}
class Program
{
/// <summary>
/// 表都在同一個資料庫中
/// </summary>
public static void OneGroup()
{
DateTime startTime = DateTime.Now.AddMinutes(-5);
DateTime endTime = DateTime.MaxValue;
//配置初始化
ShardingConfig.Init(config =>
{
config.AddAbsDb(DatabaseType.SqlServer)//新增抽象資料庫
.AddPhysicDbGroup()//新增物理資料庫組
.AddPhysicDb(ReadWriteType.Read | ReadWriteType.Write, Config.ConString1)//新增物理資料庫1
.SetShardingRule(new Base_UnitTestShardingRule())//設定分表規則
.AutoExpandByDate<Base_UnitTest>(//設定為按時間自動分表
ExpandByDateMode.PerMinute,
(startTime, endTime, ShardingConfig.DefaultDbGourpName)
);
});
var db = DbFactory.GetShardingRepository();
while (true)
{
db.Insert(new Base_UnitTest
{
Id = Guid.NewGuid().ToString(),
Age = 1,
UserName = Guid.NewGuid().ToString(),
CreateTime = DateTime.Now
});
var count = db.GetIShardingQueryable<Base_UnitTest>().Count();
Console.WriteLine($"當前資料量:{count}");
Thread.Sleep(50);
}
}
/// <summary>
/// 表分佈在兩個資料庫測試
/// </summary>
public static void TwoGroup()
{
DateTime startTime1 = DateTime.Now.AddMinutes(-5);
DateTime endTime1 = DateTime.Now.AddMinutes(5);
DateTime startTime2 = endTime1;
DateTime endTime2 = DateTime.MaxValue;
string group1 = "group1";
string group2 = "group2";
//配置初始化
ShardingConfig.Init(config =>
{
config.AddAbsDb(DatabaseType.SqlServer)//新增抽象資料庫
.AddPhysicDbGroup(group1)//新增物理資料庫組1
.AddPhysicDbGroup(group2)//新增物理資料庫組2
.AddPhysicDb(ReadWriteType.Read | ReadWriteType.Write, Config.ConString1, group1)//新增物理資料庫1
.AddPhysicDb(ReadWriteType.Read | ReadWriteType.Write, Config.ConString2, group2)//新增物理資料庫2
.SetShardingRule(new Base_UnitTestShardingRule())//設定分表規則
.AutoExpandByDate<Base_UnitTest>(//設定為按時間自動分表
ExpandByDateMode.PerMinute,
(startTime1, endTime1, group1),
(startTime2, endTime2, group2)
);
});
List<Task> tasks = new List<Task>();
for (int i = 0; i < 4; i++)
{
tasks.Add(Task.Run(() =>
{
var db = DbFactory.GetShardingRepository();
while (true)
{
db.Insert(new Base_UnitTest
{
Id = Guid.NewGuid().ToString(),
Age = 1,
UserName = Guid.NewGuid().ToString(),
CreateTime = DateTime.Now
});
var count = db.GetIShardingQueryable<Base_UnitTest>().Count();
Console.WriteLine($"當前資料量:{count}");
Thread.Sleep(50);
}
}));
}
Console.ReadLine();
}
static void Main(string[] args)
{
OneGroup();
Console.ReadLine();
}
}
}
上面Demo都在原始碼中
上面的程式碼實現了將Base_UnitTest表按照時間自動分表,每分鐘建立一張表,實際使用中根據業務需求設定ExpandByDateMode引數,常用按天、按月分表
自動分表效果
全程無需人工干預,系統會自動定時建立分表,十分簡單好用
效能測試
using Demo.Common;
using EFCore.Sharding;
using System;
using System.Diagnostics;
using System.Linq;
namespace Demo.Performance
{
class Base_UnitTestShardingRule : ModShardingRule<Base_UnitTest>
{
protected override string KeyField => "Id";
protected override int Mod => 3;
}
class Program
{
static void Main(string[] args)
{
ShardingConfig.Init(config =>
{
config.AddAbsDb(DatabaseType.SqlServer)
.AddPhysicDb(ReadWriteType.Read | ReadWriteType.Write, Config.ConString1)
.AddPhysicDbGroup()
.AddPhysicTable<Base_UnitTest>("Base_UnitTest_0")
.AddPhysicTable<Base_UnitTest>("Base_UnitTest_1")
.AddPhysicTable<Base_UnitTest>("Base_UnitTest_2")
.SetShardingRule(new Base_UnitTestShardingRule());
});
var db = DbFactory.GetRepository(Config.ConString1, DatabaseType.SqlServer);
Stopwatch watch = new Stopwatch();
var q = db.GetIQueryable<Base_UnitTest>()
.Where(x => x.UserName.Contains("00001C22-8DD2-4D47-B500-407554B099AB"))
.OrderByDescending(x => x.Id)
.Skip(0)
.Take(30);
q.ToList();
q.ToSharding().ToList();
watch.Restart();
var list1 = q.ToList();
watch.Stop();
Console.WriteLine($"未分表耗時:{watch.ElapsedMilliseconds}ms");
watch.Restart();
var list2 = q.ToSharding().ToList();
watch.Stop();
Console.WriteLine($"分表後耗時:{watch.ElapsedMilliseconds}ms");
Console.WriteLine("完成");
}
}
}
分表Base_UnitTest_0-2各有100萬資料,然後將這三張表的資料匯入Base_UnitTest中(即Base_UnitTest表的資料與Base_UnitTest_0-2三張表總合資料一致)
分表與不分表測試結果如下
這裡僅僅分了3張表,其效果立杆見影,若分表幾十張,那效果想想就很棒
其它簡單操作(非Sharing)
框架不僅支援Sharing,而且封裝了常用資料庫操作,使用比較簡單
詳細使用方式參考 連結
總結
這個簡單實用強大的框架希望能夠幫助到大家,力求為.NET生態貢獻一份力,大家一起壯大.NET生態
歡迎使用本框架,若覺得不錯,請比心
Github歡迎星星:https://github.com/Coldairarrow
部落格園歡迎點贊:https://www.cnblogs.com/coldairarrow/
QQ群3:940069478
個人QQ:862520575(歡迎技術支援及商務合作,提供.NET Core + Linux + Nginx+ jenkins + git整套持續整合快速開發平臺)