在應用開發過程中,我們總會碰到這樣的場景:某系統需要同步我們系統的資料去做一些業務邏輯,當資料量較小的時候,可以全量的提供,但當資料量很大時,全量提供就顯得很笨重,不僅耗時而且做了很多無用功,這時我們需要一種提供增量資料的機制,只告訴對方變化的資料。提供增量資料大致可分為兩種方式:mq和介面提供,mq的優點是及時,缺點是丟失、重複、回溯複雜等等問題(依賴於具體mq實現),這裡不過多贅述;介面提供不限於rpc或http等方式,介面提供的優缺點正好和mq反過來,及時性取決於呼叫週期。p.s.本文描述的資料同步區別於資料庫層的同步,應用層的同步表不一定同構,或者都不落地。
只需要乙個version引數,其它引數根據實際業務場景新增,返回值中也加入version,呼叫端使用返回值中的version用於下次呼叫。
string lastversion = getlastversion();// 拿到上一次版本號
try
for (data
data : datas)
}} finally
以上**需要放到乙個定時排程模組中,週期越短,資料延時越低。如果由於故障或bug導致處理出現問題時,只需將版本號向前調整即可,回溯簡單。
實現要考慮以下幾個方面,記憶體占用、version設計、資料刪除。
增量介面很可能被其它系統頻繁的呼叫,尤其當我們系統中有一種很核心的資料,所以要對每次呼叫返回的資料量有乙個控制,比如每次只返回1000條,後面描述都以1000條為例。我建議這個資料量控制在資料提供方,而不是呼叫方,即便呼叫方可以控制,提供方也要做乙個最大限制。
假設我們的資料類似於下表:
idupdate_time
22017-03-09 23:59:59
682017-03-09 23:59:59
262017-03-09 23:59:59
712017-03-09 23:59:59
172017-03-09 23:59:59
142017-03-09 23:59:59
112017-03-09 23:59:59
82017-03-09 23:59:59
52017-03-09 23:59:59
652017-03-09 23:59:59
662017-03-09 23:59:59
version設計很多人第一時間會想到資料更新時間,sql可能是這樣:
select * from data
where update_time > #
order
by update_time asc limit 1000;
當很多資料update_time一樣時,會丟失資料。比如上一批次返回的最後一條是id=71,version是2017-03-09 23:59:59,那本次查詢就會忽略後面update_time=2017-03-09 23:59:59的資料。這時可能又有人想到下面這樣的方式:
select * from data
where update_time >= #
order
by update_time asc limit 1000;
update_time加了個=,這樣是不會丟資料了,但是會返回重複資料,甚至死迴圈。比如比如上一批次返回的最後一條是id=71,version是2017-03-09 23:59:59,id=71後面有10000條update_time=2017-03-09 23:59:59的資料,介面每次返回1000條,這時呼叫端永遠跳不出這批資料了。鑑於上面的問題,顯然version單單使用資料更新時間已經不夠了,這時可以加入其它輔助項,比如自增id。比如比如上一批次返回的最後一條是id=71,這時的version格式是這樣:2017-03-09 23:59:59@71
,sql變成下面這樣,分兩步查詢:
第一步: 查詢update_time = '2017-03-09 23:59:59'並且id > 71的資料;
select * from data
where update_time = '2017-03-09 23:59:59'
and id > 71
order
by update_time asc, id asc limit 1000;
第二步: 查詢update_time > '2017-03-09 23:59:59'的資料;
select * from data
where update_time > '2017-03-09 23:59:59'
order
by update_time asc, id asc limit ?;
這裡有一些細節需要控制,如果第一步返回的資料量已經達到1000,則不需要執行第二步,如果小於1000,則需要執行第二步,資料量應該依據第一步返回的資料量計算。最終,version的格式:更新時間毫秒數@資料自增id,上面為了方便說明,直接用了格式化後的時間。
但上面這種基於資料更新時間的同步方式在併發寫入場景下可能存在問題,比如一條資料在2017-03-09 23:59:59時被更新,但該事務是在2017-03-10 00:00:01時提交,恰好在2017-03-09 23:59:59有乙個同步發生,那這次同步是同步不到這條資料的,因為事務還沒有提交,而下一次同步也不會同步這條資料,因為時間(2017-03-09 23:59:59)極有可能已經過去了。解決這個問題也比較簡單,我們可以在更新資料的同時,記錄一條資料日誌,並且有乙個執行緒去定期清理過期的重複資料,最後我們的版本號就是該日誌表的自增主鍵id。
增量資料的獲取是依賴更新時間,這就有乙個隱含的前提,需要資料存在,如果資料真正的刪除了,那也就不能獲取到這條資料的變更了。所以,通過介面提供增量資料不能真刪資料,而要假刪(增加乙個狀態,表示有效或無效),這也算乙個缺點吧。
介面的設計
佘士東 08 41 47 我設計乙個介面,其中有些方法很類似,比如取得某個工作物件,有可能需要獲得多個,也有可能獲得其中乙個,引數為工作物件的名字 名字列表。我是用窄介面還是寬介面好,是用乙個最大功能的方法還是多個過載方法好?比如 iservice public worker getworker s...
介面的實現
1 介面的實現 class 類名 implements 介面1,介面2,介面3 方法1 方法2 介面實現的注意事項 1 為介面中所有的方法提供具體的實現。2 必須遵守重寫的所有規則。重寫的規則 1 子類的重寫方法不能丟擲更大的異常 2 子類的重寫方法不能有更小的訪問範圍 父類 public void...
JavaScript 設計模式之介面的實現
在js中,並沒有真正意義上的介面,我們只能通過模擬的方式實現介面的效果,使用介面可以促進 的重用,降低 之間的耦合度,減少 錯誤及查詢錯誤原因,壞處就是加大 量,而且並不能強制程式設計師實現介面。js中模擬介面的方式有三種。這種方式是使用注釋顯示的告訴程式設計師需要實現哪些介面,這種方式完全靠程式設...