日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

关于Dapper实现读写分离的个人思考

發(fā)布時間:2023/12/4 编程问答 54 豆豆
生活随笔 收集整理的這篇文章主要介紹了 关于Dapper实现读写分离的个人思考 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

概念相關(guān)

????為了確保多線上環(huán)境數(shù)據(jù)庫的穩(wěn)定性和可用性,大部分情況下都使用了雙機熱備的技術(shù)。一般是一個主庫+一個從庫或者多個從庫的結(jié)構(gòu),從庫的數(shù)據(jù)來自于主庫的同步。在此基礎(chǔ)上我們可以通過數(shù)據(jù)庫反向代理工具或者使用程序的方式實現(xiàn)讀寫分離,即主庫接受事務(wù)性操作比如刪除、修改、新增等操作,從庫接受讀操作。筆者自認為讀寫分離解決的痛點是,數(shù)據(jù)庫讀寫負載非常高的情況下,單點數(shù)據(jù)庫存在讀寫沖突,從而導(dǎo)致數(shù)據(jù)庫壓力過大,出現(xiàn)讀寫操作緩慢甚至出現(xiàn)死鎖或者拒絕服務(wù)的情況。它適用與讀大于寫,并可以容忍一段時間內(nèi)不一致的情況,因為主從同步存在一定的延遲,大致的實現(xiàn)架構(gòu)圖如下(圖片來自于網(wǎng)絡(luò))。
????

????雖然我們可以通過數(shù)據(jù)庫代理實現(xiàn)讀寫分離,比如mycat,這類方案的優(yōu)勢就是對程序本身沒有入侵,通過代理本身來攔截sql語句分發(fā)到具體數(shù)據(jù)。甚至是更好的解決方案NewSQL去解決,比如TiDB。但是還是那個原則,無論使用數(shù)據(jù)庫代理或者NewSQL的情況都是比較重型的解決方案,會增加服務(wù)節(jié)點和運維成本,有時候還沒到使用這些終極解決方案的地步,這時候我們會在程序中處理讀寫分離,所以有個好的思路去在程序中解決讀寫分離也尤為重要。

基本結(jié)構(gòu)

接下來我們新建三個類,當然這個并不固定,可以根據(jù)自己的情況新建類。首先我們新建一個ConnectionStringConsts用來存放連接字符串常量,也就是用來存放讀取自配置文件或者配置中心的字符串,這里我直接寫死,當然你也可以存放多個連接字符串,大致實現(xiàn)如下。

public class ConnectionStringConsts {/// <summary>/// 主庫連接字符串/// </summary>public static readonly string MasterConnectionString = "server=db.master.com;Database=crm_db;UID=root;PWD=1";/// <summary>/// 從庫連接字符串/// </summary>public static readonly string SlaveConnectionString = "server=db.slave.com;Database=crm_db;UID=root;PWD=1"; }

然后新建存儲數(shù)據(jù)庫連接字符串主從映射關(guān)系的映射類ConnectionStringMapper,這個類的主要功能就是通過連接字符串建立主庫和從庫的關(guān)系,并且根據(jù)映射規(guī)則返回實際要操作的字符串,大致實現(xiàn)如下

public static class ConnectionStringMapper {//存放字符串主從關(guān)系private static readonly IDictionary<string, string[]> _mapper = new Dictionary<string, string[]>();private static readonly Random _random = new Random();static ConnectionStringMapper(){//添加數(shù)關(guān)系映射_mapper.Add(ConnectionStringConsts.MasterConnectionString, new[] { ConnectionStringConsts.SlaveConnectionString });}/// <summary>/// 獲取連接字符串/// </summary>/// <param name="masterConnectionStr">主庫連接字符串</param>/// <param name="useMaster">是否選擇讀主庫</param>/// <returns></returns>public static string GetConnectionString(string masterConnectionStr,bool useMaster){//是否走主庫if (useMaster){return masterConnectionStr;}if (!_mapper.Keys.Contains(masterConnectionStr)){throw new KeyNotFoundException("不存在的連接字符串");}//根據(jù)主庫獲取從庫連接字符串string[] slaveStrs = _mapper[masterConnectionStr];if (slaveStrs.Length == 1){return slaveStrs[0];}return slaveStrs[_random.Next(0, slaveStrs.Length - 1)];} }

這個類是比較核心的操作,關(guān)于實現(xiàn)讀寫分離的核心邏輯都在這,當然你可以根據(jù)自己的具體業(yè)務(wù)實現(xiàn)類似的操作。接下來,我們將封裝一個DapperHelper的操作,雖然Dapper用起來比較簡單方便,但是依然強烈建議!!!封裝一個Dapper操作類,這樣的話可以統(tǒng)一處理數(shù)據(jù)庫相關(guān)的操作,對于以后的維護修改都非常方便,擴展性的時候也會相對容易一些

public static class DapperHelper {public static IDbConnection GetConnection(string connectionStr){return new MySqlConnection(connectionStr);}/// <summary>/// 執(zhí)行查詢相關(guān)操作/// </summary>/// <param name="sql">sql語句</param>/// <param name="param">參數(shù)</param>/// <param name="useMaster">是否去讀主庫</param>/// <returns></returns>public static IEnumerable<T> Query<T>(string sql, object param = null, bool useMaster=false){//根據(jù)實際情況選擇需要讀取數(shù)據(jù)庫的字符串string connectionStr = ConnectionStringMapper.GetConnectionString(ConnectionStringConsts.MasterConnectionString, useMaster);using (var connection = GetConnection(connectionStr)){return connection.Query<T>(sql, param);}}/// <summary>/// 執(zhí)行查詢相關(guān)操作/// </summary>/// <param name="connectionStr">連接字符串</param>/// <param name="sql">sql語句</param>/// <param name="param">參數(shù)</param>/// <param name="useMaster">是否去讀主庫</param>/// <returns></returns>public static IEnumerable<T> Query<T>(string connectionStr, string sql, object param = null, bool useMaster = false){//根據(jù)實際情況選擇需要讀取數(shù)據(jù)庫的字符串connectionStr = ConnectionStringMapper.GetConnectionString(connectionStr, useMaster);using (var connection = GetConnection(connectionStr)){return connection.Query<T>(sql, param);}}/// <summary>/// 執(zhí)行事務(wù)相關(guān)操作/// </summary>/// <param name="sql">sql語句</param>/// <param name="param">參數(shù)</param>/// <returns></returns>public static int Execute(string sql, object param = null){return Execute(ConnectionStringConsts.MasterConnectionString, sql, param);}/// <summary>/// 執(zhí)行事務(wù)相關(guān)操作/// </summary>/// <param name="connectionStr">連接字符串</param>/// <param name="sql">sql語句</param>/// <param name="param">參數(shù)</param>/// <returns></returns>public static int Execute(string connectionStr,string sql,object param=null){using (var connection = GetConnection(connectionStr)){return connection.Execute(sql,param);}}/// <summary>/// 事務(wù)封裝/// </summary>/// <param name="func">操作</param>/// <returns></returns>public static bool ExecuteTransaction(Func<IDbConnection, IDbTransaction, int> func){return ExecuteTransaction(ConnectionStringConsts.MasterConnectionString, func);}/// <summary>/// 事務(wù)封裝/// </summary>/// <param name="connectionStr">連接字符串</param>/// <param name="func">操作</param>/// <returns></returns>public static bool ExecuteTransaction(string connectionStr, Func<IDbConnection, IDbTransaction, int> func){using (var conn = GetConnection(connectionStr)){IDbTransaction trans = conn.BeginTransaction();return func(conn, trans)>0;}} }

首先和大家說一句非常抱歉的話,這個類我是隨手封裝的,并沒有實驗是否可用,因為我自己的電腦并沒有安裝數(shù)據(jù)庫這套環(huán)境,但是絕對是可以體現(xiàn)我要講解的思路,希望大家多多見諒。
????在這里可以看出來Query查詢方法中我們傳遞了一個缺省參數(shù)useMaster默認值是false,主要的原因是,很多時候我們可能不能完全的使用事務(wù)性操作走主庫,讀取操作走從庫的情況,也就是我們有些場景可能要選擇性讀主庫,這時候我們可以通過這個參數(shù)去控制。當然這個字段具體的含義根據(jù)你的具體業(yè)務(wù)實際情況而定,其主要原則就是讓更多的操作能命中缺省的情況,比如你大部分讀操作都需要去主庫,那么你可以設(shè)置默認值為true,這樣的話特殊情況傳遞false,這樣的話會省下許多操作。如果你大部分讀操作都是走從庫,只有少數(shù)場景需要選擇性讀主庫,那么這個參數(shù)你可以設(shè)置為false。寫就沒有這種情況,因為無論哪種場景寫都是要在主庫進行的,除非雙主的情況,這也不是我們本次討論的重點。

使用方式

通過上述方式完成封裝之后,我們在具體數(shù)據(jù)訪問層適用的時候可以通過如下方式,如果按照默認的方式查詢可以采用如下的方式。在這里關(guān)于寫的操作我們就不展示了,因為寫的情況是固定的

string queryPersonSql = "select id,name from Person where id=@id"; var person = DapperHelper.Query<Person>(queryPersonSql, new { id = 1 }).FirstOrDefault();

如果需要存在特殊情況,查詢需要選擇主庫的話可以不使用缺省參數(shù),我們可以選擇給缺省參數(shù)傳值,比如我要讓查詢走主庫

string queryPersonSql = "select id,name from Person where id=@id"; var person = DapperHelper.Query<Person>(queryPersonSql, new { id = 1 }, true).FirstOrDefault();

當然,我們上面也提到了,缺省值useMaster是true還是false,這個完全可以結(jié)合自身的業(yè)務(wù)決定。如果大部分查詢都是走從庫的情況下,缺省值可以為false。如果大部分查詢情況都是走主庫的時候,缺省值可以給true。關(guān)于以上所有的相關(guān)封裝,模式并不固定,這一點可以完全結(jié)合自己的實際業(yè)務(wù)和代碼實現(xiàn),只是希望能多給大家提供一種思路,其他ORM也有自身提供了操作讀寫分離的具體實現(xiàn)。

總結(jié)

????以上就是筆者關(guān)于Dapper實現(xiàn)讀寫分離的一些個人想法,這種方法也適合其他類似Dapper偏原生SQL操作的ORM框架。這種方式還有一個優(yōu)點就是如果在現(xiàn)有的項目中,需要支持讀寫分離的時候,這種操作方式可能對原有代碼邏輯,入侵不會那么強,如果你前期封裝還比較合理的話,那么改動將會非常小。當然這只是筆者的個人的觀點,畢竟具體的實踐方式還需要結(jié)合實際項目和業(yè)務(wù)。本次我個人希望能得到大家更多關(guān)于這方面的想法,如果你有更好的實現(xiàn)方式歡迎評論區(qū)多多留言。

????歡迎掃碼關(guān)注我的公眾號????

總結(jié)

以上是生活随笔為你收集整理的关于Dapper实现读写分离的个人思考的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。