.net core 基於Dapper 的分庫分表開源框架(core-data)

来源:https://www.cnblogs.com/jlion/archive/2020/05/04/12771167.html
-Advertisement-
Play Games

在資料庫的數據日積月累的積累下,業務資料庫中的單表數據想必也越來越大,大到百萬、千萬、甚至上億級別的數據,這個時候就很有必要進行資料庫讀寫分離、以及單表分多表進行存儲,提高性能 ...


一、前言

感覺很久沒寫文章了,最近也比較忙,寫的相對比較少,抽空分享基於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) =>
        {
            //MySql
            return "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 Members
        static 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;
            else
                connName = 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

原創不易,覺得對你有幫助請給一個贊


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 主要有三種,順序、分支、迴圈。 順序控制結構 代碼從上到下,自左而右的執行,不對代碼進行任何的干預 分支控制結構 指代碼可以有多個選擇的執行,分為單分支和多分支 單分支:程式執行的流程不超過2個結構 多分支:程式執行的流程超過了2個以上的結構 if switch 多分支結構,一般只用於判斷確切的數值 ...
  • java類 一、類和對象 類(class)可以看成對具體事物的抽象特征提取。比如:人這個類,擁有器官的屬性、擁有走路、勞動、吃喝玩樂的行為等。 對象(Object, instance)可以看成具體的事物,就是類的實例。比如:小明這個人,就是人類的一個實例。 二、類 1. 類的定義 [修飾符1 修飾符 ...
  • 0. 前言 繼續之前的C IO流,在前幾篇小短片中我們大概看了下C 的基礎IO也對文件、目錄和路徑的操作有了一定的瞭解。這一篇開始,給大家演示一下流的各種操作。以文件流為例,一起來看看如何操作吧。 註:之前更新了一篇《Spring Cloud 實戰日記》,這是一個新的系列,有興趣的小伙伴可以從我的賬 ...
  • 《ASP.NET MVC 5 網站開發之美》 [作者] (台) demo (台) 小朱 (台) 陳傳興 (台) 王育民 (台) 陳仕傑[出版] 清華大學出版社[版次] 2015年09月 第1版[印次] 2017年03月 第2次 印刷[定價] 128.00元 【第01章】 (P004) 目前網頁前端技 ...
  • 不知道何時開始,很多程式員喜歡用ToLower,ToUpper去實現忽略大小寫模式的字元串相等性比較,有可能這個習慣是從別的語言引進的,大膽猜測下是JS,為了不引起爭論,我指的JS是技師的意思~ 一:背景 1. 講故事 在我們一個訂單聚合系統中,每一筆訂單都會標註來源,比如JD,Taobao,Eta ...
  • 本教程說明如何使用OWIN自托管Web API框架,在控制台應用程式中托管ASP.NET Web API。 .NET開放Web界面(OWIN)定義了.NET Web伺服器和Web應用程式之間的抽象。OWIN將Web應用程式與伺服器分離,這使OWIN成為在IIS之外以自己的進程自托管Web應用程式的理 ...
  • UWP已經有好幾個Bilibili的客戶端,最近又多了一個: "嗶哩 Microsoft Store" 作者雲之幻是一位很擅長設計的UWP開發者,我也從他那裡學到了很多設計方面的技巧。它還是一位Bilibili的Up主,主打PowerPoint和UWP教學。 "雲之幻的個人空間 嗶哩嗶哩 ( ゜ ゜ ...
  • using System; using System.Collections.Generic; using System.IO.Ports; using System.Text; //串口通訊類 public class SerialPortManager { //聲明一個靜態的串口資源 priva ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...