.net core 基于Dapper 的分库分表开源框架(core-data)
一、前言
感覺很久沒寫文章了,最近也比較忙,寫的相對比較少,抽空分享基于Dapper 的分庫分表開源框架core-data的強大功能,更好的提高開發過程中的效率;在數據庫的數據日積月累的積累下,業務數據庫中的單表數據想必也越來越大,大到百萬、千萬、甚至上億級別的數據,這個時候就很有必要進行數據庫讀寫分離、以及單表分多表進行存儲,提高性能,但是呢很多人不知道怎么去分庫分表,也沒有現成的分庫分表的成熟框架,故不知道怎么下手,又怕影響到業務;現在我給大家推薦core-data的分庫分表開源框架??蚣荛_源地址:https://github.com/overtly/core-data
二、基礎
2.1 回顧
這里先來回顧下我上一篇文章中的技術棧路線圖,如下:今天從這張技術棧圖中來詳細分享一切的基礎數據庫底層操作ORM。
2.2 core-data主要優勢:
上一篇文章.Net 微服務架構技術棧的那些事 中簡單的介紹了core-data主要優勢,如下:
官方建議使用DDD 領域驅動設計思想開發
支持多種數據庫(MySql / SqlServer / SQLite ),簡單配置添加鏈接的配置即可
支持分表操作,自定義分表策略的支持
支持表達式方式編寫,減少寫Sql語句機械性工作
可對Dapper 進行擴展
性能依賴于Dapper 本身的性能,Dapper 本身是輕量級ORM ,官方測試性能都強于其他的ORM
框架支持Framework4.6 - NetStandard 2.0
三、實戰詳解
這里都僅僅分享核心的內容代碼,不把整個代碼貼出來,有需要完整Demo源代碼請訪問 https://github.com/a312586670/NetCoreDemo 在我的解決方案的項目中 引用overt.core.data nuget包,如下圖:
3.1 單表模式
創建用戶實體代碼如下:
/// <summary>/// 標注數據庫對應的表名/// </summary>[Table("User")]public class UserEntity{/// <summary>/// 主鍵ID,標注自增ID/// </summary>[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]public int UserId { get; set; }/// <summary>/// 商戶ID/// </summary>public int MerchantId { set; get; }/// <summary>/// 用戶名/// </summary>public string UserName { get; set; }/// <summary>/// 真實姓名/// </summary>public string RealName { get; set; }/// <summary>/// 密碼/// </summary>public string Password { get; set; }/// <summary>/// 添加時間/// </summary>public DateTime AddTime { get; set; }}代碼中通過[Table("User")] 來和數據庫表進行映射關聯;通過[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] 標注自增主鍵.
3.2 默認分表策略
從單表模式改成分表模式,并且按照商戶的模式進行分表,代碼實體代碼改造如下:
/// <summary>/// 標注數據庫對于的表名/// </summary>[Table("User")]public class UserEntity{/// <summary>/// 主鍵ID,標注自增ID/// </summary>[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]public int UserId { get; set; }/// <summary>/// 商戶ID/// </summary>[Submeter(Bit =2)]public int MerchantId { set; get; }/// <summary>/// 用戶名/// </summary>public string UserName { get; set; }/// <summary>/// 真實姓名/// </summary>public string RealName { get; set; }/// <summary>/// 密碼/// </summary>public string Password { get; set; }/// <summary>/// 添加時間/// </summary>public DateTime AddTime { get; set; }}代碼中 MerchantId 字段上添加了[Submeter(Bit =2)]標注,并且指定了Bit=2,將會分成根據MerchantId字段取二進制進行md5 hash 取前兩位分成256張表
分表模式源碼分析
分表模式可以通過在字段上標注Submeter屬性,我們先來看看框架對于這個標注的源代碼,源代碼如下:
/// <summary>/// 分表標識/// </summary>public class SubmeterAttribute : Attribute{/// <summary>/// 16進制位數/// 1 16/// 2 256/// 3 4096/// .../// </summary>public int Bit { get; set; }}開源框架中其中一個獲得分表的表名稱的擴展方法,僅僅只貼了一個擴展方法,有興趣的可以下載開源框架進行源碼閱讀。
/// <summary>/// 獲取表名/// </summary>/// <param name="entity">實體實例</param>/// <param name="tableNameFunc"></param>/// <returns></returns>public static string GetTableName<TEntity>(this TEntity entity, Func<string> tableNameFunc = null) where TEntity : class, new(){if (tableNameFunc != null)return tableNameFunc.Invoke();var t = typeof(TEntity);var mTableName = t.GetMainTableName();var propertyInfo = t.GetProperty<SubmeterAttribute>();if (propertyInfo == null) // 代表沒有分表特性return mTableName;// 獲取分表var suffix = propertyInfo.GetSuffix(entity);return $"{mTableName}_{suffix}";}/// <summary>/// 獲取后綴(默認根據SubmeterAttribute 標注的位數進行Md5 hash 進行分表)/// </summary>/// <param name="val"></param>/// <param name="bit"></param>/// <returns></returns>internal static string GetSuffix(string val, int bit = 2){if (string.IsNullOrEmpty(val))throw new ArgumentNullException($"分表數據為空");if (bit <= 0)throw new ArgumentOutOfRangeException("length", "length必須是大于零的值。");var result = Encoding.Default.GetBytes(val.ToString()); //tbPass為輸入密碼的文本框var md5Provider = new MD5CryptoServiceProvider();var output = md5Provider.ComputeHash(result);var hash = BitConverter.ToString(output).Replace("-", ""); //tbMd5pass為輸出加密文本var suffix = hash.Substring(0, bit).ToUpper();return suffix;}源代碼中通過SubmeterAttribute 特性進行對字段進行標注分表,可以傳對應的bit參數進行框架默認的分表策略進行分表,但是很多情況下我們需要自定義分表策略,那我們應該怎么去自定義分表策略呢?我們先等一下來實踐自定義分表策略,先來創建用戶的Repository,代碼如下IUserRepository:
public interface IUserRepository : IBaseRepository<UserEntity> { }需要繼承IBaseRepository<T>的接口,該接口默認實現了基本的方法,開源框架中IBaseRepository<T>代碼方法如下圖:創建完IUserRepository接口后,我們來創建它的實現UserRepository,代碼如下:
public class UserRepository : BaseRepository<UserEntity>, IUserRepository {public UserRepository(IConfiguration configuration): base(configuration, "user"){}}從代碼中UserRepository類繼承了BaseRepository<T>類,我們來看看這個abstract類的基本結構,如下圖:開源框架中BaseRepository<T>抽象類繼承了PropertyAssist類,我們再來看看它的有哪些方法,如下圖:從圖中可以看到定義了一系列的virtual方法,那既然是virtual方法我們就可以進行重寫
CreateScriptFunc:自動創建腳本數據表方法
TableNameFunc:可以進行自定義分表策略
3.3 自定義分表策略
我們來實現上面提出的自定義分表策略問題(根據商戶Id來進行分表,并且自動把不存在的表進行初始化創建),代碼改造如下:IUserRepository:
public interface IUserRepository : IBaseRepository<UserEntity> {int MerchantId { set; get; } }UserRepository 代碼改造如下:
public class UserRepository : BaseRepository<UserEntity>, IUserRepository {public UserRepository(IConfiguration configuration): base(configuration, "user"){}/// <summary>/// 用于根據商戶ID來進行分表/// </summary>public int MerchantId { set; get; }//自定義分表策略public override Func<string> TableNameFunc => () =>{var tableName = $"{GetMainTableName()}_{MerchantId}";return tableName;};//自動創建分表的腳本public override Func<string, string> CreateScriptFunc => (tableName) =>{//MySqlreturn "CREATE TABLE `" + tableName + "` (" +" `UserId` int(11) NOT NULL AUTO_INCREMENT," +" `UserName` varchar(200) DEFAULT NULL," +" `Password` varchar(200) DEFAULT NULL," +" `RealName` varchar(200) DEFAULT NULL," +" `AddTime` datetime DEFAULT NULL," +" `MerchantId` int(11) NOT NULL," +" PRIMARY KEY(`UserId`)" +") ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8mb4; ";};}3.4 數據庫讀寫分離
我們再來看看開源框架的基類代碼結構截圖,如下:對于查詢的基本常用的方法都有一個isMaster=false的參數,該參數就是用于是否讀取主庫,用于基本的主從數據庫的分離,也就是讀寫分離,那改怎么配置讀寫分離數據庫呢 鏈接字符串如下圖:分別指定了主從數據庫的鏈接字符串. 我們來分析源代碼,核心框架源代碼如下:
/// <summary> /// 連接配置信息獲取 /// 1. master / secondary /// 2. xx.master / xx.secondary /// </summary> public class DataSettings {#region Static Private Membersstatic readonly string _connNmeOfMaster = "master";static readonly string _connNameOfSecondary = "secondary";static readonly string _connNameOfPoint = ".";static string _connNameOfPrefix = "";#endregion#region Private Member/// <summary>/// 主庫/// </summary>private string Master{get { return $"{_connNameOfPrefix}{_connNmeOfMaster}"; }}/// <summary>/// 從庫/// </summary>private string Secondary{get{return $"{_connNameOfPrefix}{_connNameOfSecondary}";}}#endregion/// <summary>/// 獲取鏈接名稱/// </summary>/// <param name="isMaster"></param>/// <param name="store">不能包含點</param>/// <returns></returns>private string Key(bool isMaster = false, string store = ""){_connNameOfPrefix = string.IsNullOrEmpty(store) ? "" : $"{store}{_connNameOfPoint}";var connName = string.Empty;if (isMaster)connName = Master;elseconnName = Secondary;return connName;} }代碼中根據isMaster 參數來進行讀寫數據庫鏈接參數的獲取,以達到讀寫分離的功能,同時還支持前綴的配置支持,也開源自由配置多個數據庫進行讀取,只需要構造函數中獲取配置即可。上面的分表Demo 單元測試運行后的結果例子如下圖:已經按照MerchantId 字段進行分表
三、總結
到這里用戶表已經根據商戶ID進行分表存儲了,這樣就做到了讀寫分離及自定義分表策略存儲數據,core-data 開源框架還支持更多的強大功能,實現了一系列的基礎CRUD的方法,不用寫任何的sql語句,Where表達式的支持,同時可以自定義復雜的sql語句,更多請訪問框架開源地址:https://github.com/overtly/core-data. 完整的Demo 代碼 已經放在github上,Demo代碼結構圖如下:地址:https://github.com/a312586670/NetCoreDemo
原創不易,覺得對你有幫助請給一個贊
往期精彩回顧
【.net core】電商平臺升級之微服務架構應用實戰
.Net Core微服務架構技術棧的那些事
Asp.Net Core 中IdentityServer4 授權中心之應用實戰
Asp.Net Core 中IdentityServer4 授權中心之自定義授權模式
Asp.Net Core 中IdentityServer4 授權流程及刷新Token
Asp.Net Core 中IdentityServer4 實戰之 Claim詳解
Asp.Net Core 中IdentityServer4 實戰之角色授權詳解
Asp.Net Core 中間件應用實戰中你不知道的那些事
Asp.Net Core Filter 深入淺出的那些事-AOP
Asp.Net Core EndPoint 終結點路由工作原理解讀
ASP.NET CORE 內置的IOC解讀及使用
??給個[在看],是對我最大的支持??
總結
以上是生活随笔為你收集整理的.net core 基于Dapper 的分库分表开源框架(core-data)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Autofac的切面编程实现
- 下一篇: 青年节寄语和新课程免费上架