有些場景下,須要隔離不一樣的DB,彼此DB之間不能互相訪問,但實際的業務場景又須要從A DB訪問B DB的情形,這時怎麼辦?我認爲有以下常規的三種方案:html
1.雙方提供RESET API,須要訪問不一樣DB數據時,能夠經過API來獲取指定數據;sql
這種方案優勢是隔離性、定製性強,統一出入口,只能經過指定的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表中。ui
這種方案的優勢是:能夠根據實際狀況靈活定製同步的表數據,不侷限於某一張表或某一個DB,能夠保證不一樣DB間同步表的數據一致性,讓原本跨DB操做表變成了同一個DB的事情,並且能夠增、刪、改、查,功能不受限;缺點是靈活性太強,程序代碼實現可靠的跨DB的實時同步邏輯的實現複雜度較高,對於開發人員的要求較高,若是寫的同步邏輯沒法保證明時、可靠、高可用,那對於業務來說是災難性的。日誌
上述三種方案,第一、2方案基本都是定製化的常規方案,我(夢在旅途,http://www.zuowenjun.cn)今天要分享的是第3種方案:跨DB增量(增、改)同步兩張表的數據,注意是增量同步,其中刪除這個我沒有說明,緣由是若是DB表中記錄是物理刪除(即:真實的DELETE),那就沒法簡單的經過程序代碼獲取到刪除的記錄,除非在DB中加入DELETE觸發器記錄刪除記錄的主鍵到臨時表或開啓更改追蹤(CHANGE_TRACKING)或DB日誌分析,故本文講的是不給表、DB增長額外負擔的狀況實時增量同步,至於刪的同步這個我認爲最好是邏輯標記刪除(過時最後清理【真實刪除】),而不要物理刪除。orm
關於程序代碼實現跨DB同步表數據方案,以前已有總結過,詳見:https://www.cnblogs.com/zuowj/p/6264711.html ---》4.利用BCP(sqlbulkcopy)來實現兩個不一樣數據庫之間進行數據差別傳輸(即:數據同步)htm
以前的文章同步主要是基於TranFlag標記字段 或觸發器來實現同步,這種方式必需對錶數據的增、刪、改邏輯都有要求與規範,也就是增、改必需更改TranFlag=0,刪必需記錄表刪除臨進表中,這樣才能實現同步邏輯,而今天是在這個同步基礎上(BCP),不給表、DB增長額外負擔的狀況實時增量同步,對數據源的插入、改動沒有要求。blog
代碼以下:(如下同步適用於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有問題,致使一直同不不成功,可能會出現一直同步不過去的狀況,這種狀況能夠加上預警+人工干預,這個是機率的事情。
好了,若是你們有什麼好的意見或建議歡迎下方留言評論,謝謝!