天天看點

增量接口的設計及實作引言接口設計接口使用接口實作

在應用開發過程中,我們總會碰到這樣的場景:某系統需要同步我們系統的資料去做一些業務邏輯,當資料量較小的時候,可以全量的提供,但當資料量很大時,全量提供就顯得很笨重,不僅耗時而且做了很多無用功,這時我們需要一種提供增量資料的機制,隻告訴對方變化的資料。提供增量資料大緻可分為兩種方式:MQ和接口提供,MQ的優點是及時,缺點是丢失、重複、回溯複雜等等問題(依賴于具體MQ實作),這裡不過多贅述;接口提供不限于RPC或HTTP等方式,接口提供的優缺點正好和MQ反過來,及時性取決于調用周期。P.S.本文描述的資料同步差別于資料庫層的同步,應用層的同步表不一定同構,或者都不落地。

Created with Raphaël 2.1.0AABBsync datado something

隻需要一個version參數,其它參數根據實際業務場景添加,傳回值中也加入version,調用端使用傳回值中的version用于下次調用。

以上代碼需要放到一個定時排程子產品中,周期越短,資料延時越低。如果由于故障或BUG導緻處理出現問題時,隻需将版本号向前調整即可,回溯簡單。

實作要考慮以下幾個方面,記憶體占用、version設計、資料删除。

增量接口很可能被其它系統頻繁的調用,尤其當我們系統中有一種很核心的資料,是以要對每次調用傳回的資料量有一個控制,比如每次隻傳回1000條,後面描述都以1000條為例。我建議這個資料量控制在資料提供方,而不是調用方,即便調用方可以控制,提供方也要做一個最大限制。

假設我們的資料類似于下表:

id

update_time

2

2017-03-09 23:59:59

68

26

71

17

14

11

8

5

65

66

version設計很多人第一時間會想到資料更新時間,SQL可能是這樣:

當很多資料update_time一樣時,會丢失資料。比如上一批次傳回的最後一條是id=71,version是2017-03-09 23:59:59,那本次查詢就會忽略後面update_time=2017-03-09 23:59:59的資料。這時可能又有人想到下面這樣的方式:

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格式是這樣:<code>2017-03-09 23:59:59@71</code>,SQL變成下面這樣,分兩步查詢:

這裡有一些細節需要控制,如果第一步傳回的資料量已經達到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。

增量資料的擷取是依賴更新時間,這就有一個隐含的前提,需要資料存在,如果資料真正的删除了,那也就不能擷取到這條資料的變更了。是以,通過接口提供增量資料不能真删資料,而要假删(增加一個狀态,表示有效或無效),這也算一個缺點吧。