前言
前兩個(gè)項(xiàng)目忧便,客戶端與服務(wù)器端需要進(jìn)行大量的數(shù)據(jù)交互青灼,設(shè)計(jì)方案時(shí)并沒(méi)有考慮到數(shù)據(jù)量多大的情況,導(dǎo)致后期在實(shí)際生產(chǎn)環(huán)境中辽旋,經(jīng)常出現(xiàn)客戶端崩潰現(xiàn)象浩嫌,今天在一篇博客上看到了一種簡(jiǎn)單的解決方案檐迟,轉(zhuǎn)載過(guò)來(lái),便于后期查看码耐,原文地址:http://blog.cnbang.net/tech/2258/
在郵件/日歷/SNS等客戶端里追迟,客戶端數(shù)據(jù)要不斷與服務(wù)端進(jìn)行數(shù)據(jù)同步,在同步過(guò)程中伐坏,只拉取有修改的數(shù)據(jù)怔匣,稱為增量更新,增量更新方案一般有兩種桦沉,一是對(duì)比每瞒,二是日志。
對(duì)比
對(duì)比就是客戶端請(qǐng)求服務(wù)端所有關(guān)鍵數(shù)據(jù)纯露,跟本地已有的數(shù)據(jù)進(jìn)行對(duì)比剿骨,篩選出增刪改的數(shù)據(jù)進(jìn)行更新。
用對(duì)比方法的好處是服務(wù)端什么都不用做埠褪,壞處是客戶端邏輯復(fù)雜浓利,耗網(wǎng)絡(luò)流量。在這種方案里钞速,數(shù)據(jù)的新增和刪除很容易判斷贷掖,根據(jù)客戶端數(shù)據(jù)的id列表和服務(wù)端數(shù)據(jù)的id列表進(jìn)行對(duì)比就行,若要判斷哪個(gè)數(shù)據(jù)有修改則比較麻煩渴语,需要取回?cái)?shù)據(jù)進(jìn)行對(duì)比苹威,如果從服務(wù)端拉回所有對(duì)所有數(shù)據(jù)進(jìn)行對(duì)比會(huì)很耗網(wǎng)絡(luò)流量,有一個(gè)優(yōu)化方式驾凶,就是對(duì)每個(gè)數(shù)據(jù)的修改進(jìn)行標(biāo)記牙甫。
以日歷為例,一個(gè)日歷可修改的字段很多调违,例如時(shí)間段窟哺,內(nèi)容,邀請(qǐng)人等技肩,全部拉回來(lái)對(duì)比不現(xiàn)實(shí)且轨,對(duì)此可以在服務(wù)端給每個(gè)日歷事件新增一個(gè)字段tag,表示這個(gè)日歷事件的版本虚婿,服務(wù)端更新一個(gè)日歷事件時(shí)會(huì)同時(shí)更新這個(gè)tag殖告,客戶端只需要取回每個(gè)id對(duì)應(yīng)的tag,跟本地保存的tag對(duì)比雳锋,不一致表示這個(gè)日歷事件已經(jīng)更新,再去獲取日歷實(shí)體就完成更新了羡洁。
若服務(wù)端因?yàn)槟承┰驘o(wú)法給每個(gè)數(shù)據(jù)保存一個(gè)版本標(biāo)記玷过,可以實(shí)時(shí)計(jì)算,在客戶端和服務(wù)端約定一個(gè)算法,把所有可變參數(shù)拿出來(lái)辛蚊,通過(guò)特定算法hash出一個(gè)值粤蝎,對(duì)比這個(gè)hash值判斷是否需要更新。
郵件協(xié)議IMAP袋马,日歷協(xié)議CalDAV就是用這種方式做增量更新初澎,IMAP并沒(méi)有做上述的優(yōu)化,在判斷郵件有沒(méi)有更新時(shí)只能乖乖把所有數(shù)據(jù)請(qǐng)求回來(lái)對(duì)比虑凛,數(shù)據(jù)是XML碑宴,算是相當(dāng)?shù)托У膮f(xié)議。CalDAV給每個(gè)日歷事件加了上述的tag桑谍,直接對(duì)比即可知道是否需要更新延柠。
日志
日志指服務(wù)端記錄數(shù)據(jù)的每一次增刪改,用一個(gè)類似版本號(hào)的sync-key標(biāo)記這次修改锣披,客戶端通過(guò)一個(gè)舊的sync-key向服務(wù)端請(qǐng)求贞间,服務(wù)端返回這個(gè)sync-key與最新sync-key之間所有的修改給客戶端,完成增量更新雹仿。
這個(gè)sync-key在服務(wù)端的實(shí)現(xiàn)上可以是時(shí)間增热,也可以是一個(gè)自增的id,sync-key之間有順序關(guān)系就行胧辽。在一個(gè)數(shù)據(jù)集里峻仇,每次數(shù)據(jù)有更新,就新增一個(gè)sycn-key票顾,并記錄這次更新础浮。圖示這個(gè)過(guò)程:
這個(gè)方案客戶端邏輯很簡(jiǎn)單,但服務(wù)端負(fù)擔(dān)較大奠骄,每次數(shù)據(jù)更新都要記錄豆同,客戶端請(qǐng)求時(shí)需要查詢給出相應(yīng)的數(shù)據(jù)。這個(gè)方案在實(shí)際操作中還有兩個(gè)問(wèn)題:
一是時(shí)間長(zhǎng)了服務(wù)端保存數(shù)據(jù)量過(guò)大含鳞∮靶猓可以通過(guò)限制記錄的條數(shù)解決,超過(guò)限制就刪除最舊的記錄蝉绷。這樣做會(huì)出現(xiàn)一個(gè)問(wèn)題鸭廷,若客戶端帶著在服務(wù)端已被刪除的sync-key上來(lái)請(qǐng)求,該如何處理熔吗?一般做法是返回一個(gè)錯(cuò)誤給客戶端辆床,讓客戶端重新拉取所有數(shù)據(jù)。
二是若客戶端sync-key過(guò)舊桅狠,增量數(shù)據(jù)可能過(guò)大讼载〗窝恚客戶端數(shù)據(jù)太老,有太多數(shù)據(jù)需要更新咨堤,若一次性返回所有增量數(shù)據(jù)菇篡,這個(gè)請(qǐng)求可能會(huì)很大,請(qǐng)求時(shí)間太長(zhǎng)一喘,成功率也會(huì)很低驱还。解決方式是分多次請(qǐng)求,客戶端和服務(wù)端可以約定一個(gè)字段作為閥值凸克,服務(wù)端每次返回的增量數(shù)據(jù)量不超過(guò)這個(gè)閥值议蟆,若總數(shù)據(jù)超過(guò)這個(gè)閥值,則分多次請(qǐng)求触徐,通過(guò)每次請(qǐng)求返回的sync-key定位下次請(qǐng)求該返回哪些數(shù)據(jù)咪鲜。例如客戶端sync-key是100,服務(wù)端最新sync-key是1000撞鹉,閥值是50疟丙,客戶端第一次帶sync-key=100請(qǐng)求,服務(wù)端第一次返回sycn-key 100-150這一段增量數(shù)據(jù)鸟雏,并返回sync-key=150享郊,并有一個(gè)值告訴客戶端這個(gè)sync-key還不是最新,客戶端再帶上sync-key=150請(qǐng)求孝鹊,以此類推炊琉,直到sync-key=1000。
微軟的Exchange/ActiveSync就是用這種方式實(shí)現(xiàn)增量更新又活,ActiveSync還用WBXML壓縮了數(shù)據(jù)苔咪,更適用于移動(dòng)端。此外日歷協(xié)議CalDAV的也有一個(gè)擴(kuò)展協(xié)議RFC6578使用這種方式柳骄。ActiveSync和CalDAV擴(kuò)展協(xié)議都有分多次請(qǐng)求增量數(shù)據(jù)的策略团赏。
小結(jié)
對(duì)于Timeline式的數(shù)據(jù),增量更新方式多是以上兩種耐薯,或者這兩種的變體舔清,可以根據(jù)業(yè)務(wù)特性修改或簡(jiǎn)化其中的邏輯,例如對(duì)于微博Timeline曲初,它可以不考慮微博的修改体谒,不考慮同步評(píng)論轉(zhuǎn)發(fā)數(shù)的變化,不考慮同步刪除的微博臼婆,并且每一條微博都有一個(gè)遞增的id抒痒,那它的增量更新邏輯就很簡(jiǎn)單,只需要把客戶端最新一條微博的id作為since_id傳到服務(wù)端颁褂,返回比這個(gè)id更新的微博就行了评汰,這里微博id相當(dāng)于日志方式的sync-key纷捞,算是對(duì)日志方式的一種簡(jiǎn)化。