增量更新模型的討論

客戶端和服務端的數據同步過程當中,客戶端有緩存,不須要每次都是全量刷新,因此能夠採用增量的方式更新。html

每次在客戶端進行刷新的時候,服務端會將最新的增刪改操做推送到客戶端,客戶端對其緩存進行操做,以保持數據的同步。算法

最原始的方法 - Full Transfer

Full Transfer

[圖片來自參考文獻 1]數據庫

咱們要實現的是數據的同步,那麼咱們只要每次在本地列表須要更新時,將全部後端數據庫的信息拉取到本地,進行對比,若是有新增就新增,有修改就修改,若是新來的版本里面某一項不見了,就直接在本地緩存中刪除該項便可。後端

優勢:緩存

  • 寫接口時很清爽,它是最簡單的實現

缺點:服務器

  • 服務端發送多餘的數據
  • 只能用於小數據量,在數據量比較大的時候,單次刷新的操做就耗費不少網路流量。

應用場景:IMAP 協議,新聞類 APP 中的每日頭條,如 Yahoo News Digestapp

在 CalDAV 日曆協議中,每一個日曆事件都擁有一個 ETag 來標記它的版本信息,客戶端對比服務器發來的 ETag 來決定要不要更新某項日曆事件,此處的 ETag 和 HTTP headers 中 Last-Modified 和 ETag 實際上是同樣的東西,用於標記版本信息。這種方式即爲接下來要提到的 Timestamp Transfer。ide

根據修改時間來拉取增刪改信息 - Timestamp Transfer

Timestamp Transfer

[圖片來自參考文獻 1].net

客戶端存儲上次拉取的數據的 Timestamp,在請求更新數據時,攜帶該 Timestamp 做爲本地數據版本信息。數據庫內每行數據設置一個 LAST_UPDATE_TIME 字段,服務器將比該時間更新的數據返回給客戶端。日誌

優勢:

  • 相對於 Full Transfer 來講減小了冗餘數據的傳輸

缺點:

  • 傳輸時 Timestamp 做爲版本信息須要精確控制,請求錯誤的版本號可能帶來本地數據的不許確
  • 已經刪除的數據其實已經不存在了,取不到 LAST_UPDATE_TIME

第二個缺點能夠經過設置 IS_DELETE 字段來避免,每次刪除數據時,僅僅更新 LAST_UPDATE_TIME 和設置 IS_DELETE 爲 True 來標記已刪除。此處帶來的缺點是,被刪除的數據繼續佔用空間,不過當只有一個客戶端時,能夠在客戶端確認刪除緩存中相應數據後刪除數據庫中 IS_DELETE 爲 True 的數據,這個方法被成爲 Soft Delete.

結合算法和修改時間來拉取增刪改信息 - Mathematical Transfer

Mathematical Transfer

[圖片來自參考文獻 1]

服務器接收到客戶端發來的更新請求時,將客戶端根據 Reconciliation 算法生成的值來肯定要返回給客戶端的增刪改信息。此處說的 Reconciliation 算法的做用與 Checksum 校驗和相似,用於校驗數據是否已經修改。

優勢:

  • 避免了查詢數據庫時對 LAST_UPDATE_TIME 的條件過濾

缺點:

  • Reconciliation 算法普適性低
  • Reconciliation 算法開發週期長

增刪改日誌 - SYNC

服務端記錄數據的每次操做都記錄進一個增量數據庫,數據庫內記錄了每條操做的對象 ID 和操做的內容。此處思想相似於 Patch 補丁操做,客戶端發送一個 Timestamp 信息,服務器將這個時間之後的全部增刪改操做返回給客戶端,客戶端再進行打補丁操做,使得最終結果與服務端同步。

優勢:

  • 保持了全部數據的精確可同步

缺點:

  • 客戶端好久不更新之後單次的更新補丁很大
  • 若是數據改動不少,那記錄操做的表將會變得很大

場景

咱們如今的需求是,有一個訂單模塊,某一用戶在 APP 中能夠點擊刷新訂單列表,將服務器上其全部訂單顯示出來。

# 訂單更新操做
def order_update(request):
    # 對其中一個訂單作更新操做
    order = Order.objects.get(id=1)
    order.update_time = time.time()
    order.save()
    # 設置緩存內的版本信息
    cache_time = time.time()
    cache.set('order_list_' + str(request.user.id), cache_time, 3600 * 24 * 7)
    return JsonResponse({})


# 返回訂單列表
def order_list(request):
    # 保存客戶端的版本信息 since,即 timestamp
    since = request.GET.get('since') or 0
    since = float(str(since))
    # 取出緩存中最新版本信息 since_cache
    since_cache = cache.get('order_list_'+ str(request.user.id))
    # 若是客戶端攜帶版本信息爲 0,說明客戶端請求全量更新
    # 或者當緩存中版本新於客戶端版本信息,則返回 update_time 新於客戶端版本的全部條目
    if (since == 0) or since_cache and (since_cache > since):
        orders = Order.objects.filter(user=request.user, update_time__gt=since)
        data = [order.id for order in orders]
        return JsonResponse({
            'status': 1001,
            'since': since_cache,
            'data': data
        })
    # 沒有更新,直接返回最新的緩存時間
    else:
        return JsonResponse({
            'status': 1001,
            'since': since_cache,
            'data': []
        })

篩選的狀況

有時候咱們只須要更新知足篩選條件的條目,客戶端利用 Timestamp Transfer 來進行拉取全部增刪改信息,可是隻針對知足篩選條件的那些項目進行更新操做,其他的直接丟棄。

參考文獻

相關文章
相關標籤/搜索