關於跨DB增量(增、改)同步兩張表的數據小技巧

来源:https://www.cnblogs.com/zuowj/archive/2018/08/14/9473984.html
-Advertisement-
Play Games

有些場景下,需要隔離不同的DB,彼此DB之間不能互相訪問,但實際的業務場景又需要從A DB訪問B DB的情形,這時怎麼辦?我認為有如下常規的三種方案: 1.雙方提供RESET API,需要訪問不同DB數據時,可以通過API來獲取指定數據; 這種方案優點是隔離性、定製性強,統一齣入口,只能通過指定的A ...


有些場景下,需要隔離不同的DB,彼此DB之間不能互相訪問,但實際的業務場景又需要從A DB訪問B DB的情形,這時怎麼辦?我認為有如下常規的三種方案:

1.雙方提供RESET API,需要訪問不同DB數據時,可以通過API來獲取指定數據;

這種方案優點是隔離性、定製性強,統一齣入口,只能通過指定的API訪問指定的數據;缺點與優點是對立的,也就是定製性太強,導致每次業務發生變更,需要訪問不同數據的時候,需要雙方更改API的入參或返參,降低了開發效率;而且無法使用表JOIN,這樣在某些情況下也會導致查詢數據效率變低。目前主流的方案都是建議使用API方案

2.利用DB的同步技術(如:SQL SERVER的訂閱複製、MYSQL的主從複製腳本等)來實現不同DB的數據同步共用

這種方案優點是可以在同一個DB訪問到另一個DB中所需表的數據,可以直接JOIN,把原來的跨DB訪問變成了同一個DB的事情;缺點是依賴DB的同步技術,而且兩台DB伺服器的網路必需互通,沒有完全的隔離,且往往同步過來的表不允許直接修改,或需修改仍然需要跨DB修改或使用方案1的API來進行修改。

3.通過程式代碼實現兩個DB的數據同步(增、刪、改、查),如:可以定時輪詢源DB的A表,然後獲取變更的記錄(一般是:增、刪、改的記錄),再通過程式代碼把源DB的A表的變更記錄批量更新(若是新增、則是插入,若是修改,則是更新,若是刪除,則是刪除)到目的DB的A表中。

這種方案的優點是:可以根據實際情況靈活定製同步的表數據,不局限於某一張表或某一個DB,可以保證不同DB間同步表的數據一致性,讓本來跨DB操作表變成了同一個DB的事情,而且可以增、刪、改、查,功能不受限;缺點是靈活性太強,程式代碼實現可靠的跨DB的實時同步邏輯的實現複雜度較高,對於開發人員的要求較高,如果寫的同步邏輯無法保證實時、可靠、高可用,那對於業務來講是災難性的。

上述三種方案,第1、2方案基本都是定製化的常規方案,我(夢在旅途,http://www.zuowenjun.cn)今天要分享的是第3種方案:跨DB增量(增、改)同步兩張表的數據,註意是增量同步,其中刪除這個我沒有說明,原因是如果DB表中記錄是物理刪除(即:真實的DELETE),那就無法簡單的通過程式代碼獲取到刪除的記錄,除非在DB中加入DELETE觸發器記錄刪除記錄的主鍵到臨時表或開啟更改追蹤(CHANGE_TRACKING)或DB日誌分析,故本文講的是不給表、DB增加額外負擔的情況實時增量同步,至於刪的同步這個我認為最好是邏輯標記刪除(過期最後清理【真實刪除】),而不要物理刪除。

關於程式代碼實現跨DB同步表數據方案,之前已有總結過,詳見:https://www.cnblogs.com/zuowj/p/6264711.html  ---》4.利用BCP(sqlbulkcopy)來實現兩個不同資料庫之間進行數據差異傳輸(即:數據同步)

 之前的文章同步主要是基於TranFlag標記欄位 或觸發器來實現同步,這種方式必需對錶數據的增、刪、改邏輯都有要求與規範,也就是增、改必需更改TranFlag=0,刪必需記錄表刪除臨進表中,這樣才能實現同步邏輯,而今天是在這個同步基礎上(BCP),不給表、DB增加額外負擔的情況實時增量同步,對數據源的插入、改動沒有要求。

代碼如下:(以下同步適用於SQL SERVER 不同DB的表增量同步)

            try
            {
                SqlConnection obConnSrc = new SqlConnection(connLMSStr);
                SqlConnection obConnDest = new SqlConnection(mconnCCSStr);

                string lastTamp = ClsDatabase.gGetFieldValue(obConnSrc, "update TS_SyncUptime set UPTime=GETDATE() OUTPUT (deleted.LastUPstamp) as oldtamp FROM TS_CCSUptime WHERE TableName=N'tableNameA'", "oldtamp");


                string selectSql = @"SELECT id,aaa,bbb,ccc,ddd,eee,fff  
                                  FROM tableNameA WHERE 其它同步過濾查詢條件 AND CONVERT(bigint,sys_tamp)>{0}";

                selectSql = string.Format(selectSql, lastTamp);

                master.TransferBulkCopy(selectSql, obConnSrc,
                                "tableNameA", obConnDest,
                                 (stable) =>
                                 {
                                     var colMaps = new Dictionary<string, string>();
                                     foreach (DataColumn col in stable.Columns)
                                     {
                                         colMaps.Add(col.ColumnName, col.ColumnName);
                                     }
                                     return colMaps;
                                 },
                                 (tempTableName, stable, destConn, srcConn) =>
                                 {
                                     StringBuilder saveSqlBuilder = new StringBuilder("begin tran" + Environment.NewLine);

                                     string IUSql = master.BuildInsertOrUpdateToDestTableSql("tableNameA", tempTableName, new[] { "id" }, stable.ExtendedProperties[master.MapDestColNames_String], 2);
                                     saveSqlBuilder.Append(IUSql);

                                     saveSqlBuilder.AppendLine("commit");

                                     ClsDatabase.gExecCommand(destConn, saveSqlBuilder.ToString());


                                     ClsDatabase.gExecCommand(srcConn, "update TS_SyncUptime set UPTime=GETDATE(),LastUPstamp=CONVERT(bigint,sys_tamp) FROM TS_SyncUptime WHERE TableName=N'tableNameA'");

                                     return false;
                                 });


            }
            catch (Exception ex)
            {
                writeLog(ex);//記錯誤日誌
            }

 上述同步代碼邏輯很簡單,可以參照之前的文章,這裡主要是說明幾個重要點:

1.TS_SyncUptime表用於記錄與管理同步任務的信息,主要包含如下幾個欄位:

 

TableName:要同步的表名,UPTime每一次同步的觸發時間點(可更改),sys_tamp行變更時間戳(不可更改),LastUPstamp行最後有效變數時間戳(可以更新)

2.具體關鍵同步邏輯如下:

2.1先更新TS_SyncUptime表,以便觸發sys_tamp行變更時間戳發生改變(相當於記錄同步觸發時間點),在更改的同時取出LastUPstamp行最後有效變更時間戳(相當於上次同步的觸發時間點)

2.2使用LastUPstamp作為過濾條件,查詢>源DB的源表中時間戳欄位,這樣就可以查詢出自上一次同步觸發點到當前時間待同步的記錄(增、改)

2.3利作BCP執行同步(詳見之前文章說明)

2.4確保同步成功後,再次更新TS_SyncUptime表,並把sys_tamp行變更時間戳(當前觸發時間點)更新到LastUPstamp行最後有效變數時間戳(記住本次觸發時間點)

如上步驟即可實現可靠的同步,有人可能有疑問,這樣就能實現可靠同步嗎?我這裡解釋一下:

3.1同步觸發時記錄當前觸發時間點,並取得上一次的觸發時間點(這裡的上一次觸發時間點是指上一次開始準備同步的記錄時間點,確保從上一次查詢到同步完成之間的時間點都包括其中,防止漏數據)

3.2如果同步的任一環節失敗(只要最終沒有同步成功),那麼再次同步觸發時均取到的是同 一個時間點(LastUPstamp),而且即使重覆執行同步邏輯,也不會出現重覆(因為存在則更新不存在則插入原則),保證冪等,這樣就確保了同步的可靠性

3.3當然如果某個時間點的數據或某個DB有問題,導致一直同不不成功,可能會出現一直同步不過去的情況,這種情況可以加上預警+人工干預,這個是概率的事情。

好了,如果大家有什麼好的意見或建議歡迎下方留言評論,謝謝!


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

-Advertisement-
Play Games
更多相關文章
  • 概要 相信很多朋友在程式生涯中,或多或少都會遇到處理媒體流的需求,而且是採用S端處理,排除代碼上課優化的極限,仍然還是需要很長的時間時,比如: 1:百度網盤在播放視頻的時候,如非VIP會員還需要更長甚至直接斷開流; 2:任何直播視頻在轉碼的時候,不論是否VIP,都會有段緩衝時間,已至於觀看者無法達到 ...
  • 什麼是NoSql NoSQL(Not Only SQL),泛指非關係型的資料庫,是對不同於傳統的關係型資料庫的資料庫管理系統的統稱,強調Key-Value Stores和文檔資料庫的優點。為瞭解決大規模數據集合多重數據種類帶來的挑戰而興起的資料庫。有著模式自由,逆規範化,多分區存儲,彈性可擴展,多副 ...
  • 使用ILMerge工具,將C#項目debug目錄下的exe及其依賴的dll文件打包成一個exe文件,直接雙擊就可運行。 使用工具: ILMerge :http://www.microsoft.com/en-us/download/details.aspx?id=17630 ILMerge-GUI:h ...
  • 眾所周知垂直擴展是提升單機的性能的方式,比如提升雙路、四路的CPU運算能力,加大記憶體,更換速度更快的SSD,或者從代碼根本上進行優化和性能提升。水平擴展是提供多台多種伺服器分離單機性能的方式,比如集群,主從,隊列,負載平衡等等。 白話的垂直擴展 現在伺服器都是雲伺服器,單純從單機的硬體性能提升整體性 ...
  • 上一節我們已經介紹了FFmpeg在Net Core中的簡單應用,這一節我們將根據之前的功能需求和解決方案,進行項目的詳細設計工作。 畫個流程圖 先闡述一下流程,如下圖: 整個流程其實非常簡單,客戶端(無論桌面軟體、還是原生APP、還是HTML網頁)通過一個統一的介面進行調用,我們這裡定義這個介面名稱 ...
  • 準備工作: 1:Net Core 2.1 為何要用2.1,因為在macOS 10.13上一個奇怪的問題,請看另一篇博文介紹 2:FFmpeg 版本無所謂,最新即可,安裝教程網上很多,當然也可以使用docker進行ffmpeg的部署,下載地址 http://ffmpeg.org/download.ht ...
  • 在引用官方的qiniu.core的nuget是總是發生錯誤,錯誤如下,所以就在網上找到了一種解決方案,並稍微改進了一番, 以下是引用圖: 第一步:引用nuget(是位大神寫的,官方的qiniu.core用不了) 第二步:新建項目新建控制器視圖、添加配置信息類和讀取配置信息 配置信息類: 配置信息: ...
  • 使用NPOI導入Excel 首先在MVC項目中導入NPOI 查詢NPOI安裝,排序依據,選擇:最高下載量,選擇第一個。 在控制器中創建ExcelController 在Index視圖中寫入代碼: @using (Html.BeginForm("Import", "Excel", FormMethod ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...