日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

多终端数据同步机制设计

發(fā)布時(shí)間:2023/12/4 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 多终端数据同步机制设计 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

多終端數(shù)據(jù)同步機(jī)制設(shè)計(jì)

之前寫過一篇文章數(shù)據(jù)同步流程設(shè)計(jì)的文章,這里整理一下在公眾號(hào)里分享一下

Intro

因?yàn)轫?xiàng)目需要,需要設(shè)計(jì)一個(gè)多終端數(shù)據(jù)同步的機(jī)制, 需要滿足以下條件:

  • 多個(gè)終端數(shù)據(jù)操作及同步,終端可能離線

  • 每次同步的時(shí)候只拉取需要同步的數(shù)據(jù),且數(shù)據(jù)不能存在丟失,增量同步

  • 盡可能少的調(diào)用服務(wù)器端接口

  • 同步流程

    整體同步流程

    我想仿照Git數(shù)據(jù)同步的方式來進(jìn)行數(shù)據(jù)同步,于是放著Git同步的流程來進(jìn)行設(shè)計(jì),首先每次提交會(huì)有一個(gè)版本號(hào),另外每次提交之前應(yīng)盡可能先從服務(wù)器端拉取數(shù)據(jù), 保證客戶端的數(shù)據(jù)是最新的情況下再進(jìn)行提交本地的修改。按照Git的方式來進(jìn)行數(shù)據(jù)同步時(shí),可能會(huì)存在數(shù)據(jù)沖突,如果存在數(shù)據(jù)沖突需要客戶端解決沖突。
    也就是總體來說,操作有兩個(gè)大的操作,一個(gè)是從服務(wù)器端拉取數(shù)據(jù),一個(gè)是向服務(wù)器端推送數(shù)據(jù)更新。
    在數(shù)據(jù)庫層面有一個(gè)數(shù)據(jù)版本表來存儲(chǔ)每一次提交,每一次更新會(huì)在更新結(jié)束之后將在版本表中加上一條記錄,更新一個(gè)版本,并將版本號(hào)返回給客戶端, 每次從服務(wù)器端拉取更新的時(shí)候不僅會(huì)將更新的數(shù)據(jù)返回給客戶端,也會(huì)將最新的版本號(hào)返回到客戶端,用以客戶端下一次同步數(shù)據(jù)。

    最后服務(wù)器端提供了三個(gè)接口

  • GetCurrentVersion()?查詢用戶數(shù)據(jù)的最新版本號(hào),

  • PullData()?從服務(wù)器端拉取更新數(shù)據(jù),

  • PushData()?向服務(wù)器端推送本地?cái)?shù)據(jù)更新

  • 思慮再三之后最終產(chǎn)出了下面的流程圖:

    從服務(wù)器端獲取用戶數(shù)據(jù)的最新版本號(hào)

    客戶端調(diào)用 GetCurrentVersion() 接口,需要傳遞一個(gè)標(biāo)識(shí)用戶賬號(hào)的參數(shù),這樣才能查詢到某一個(gè)用戶的數(shù)據(jù)信息。根據(jù)用戶賬號(hào)信息查詢數(shù)據(jù)的最新版本號(hào),返回到客戶端,客戶端根據(jù)服務(wù)器端的版本號(hào)和本地進(jìn)行比較,如果一致則說明是最新版本之后判斷本地是否有修改有修改則直接提交即可,如果不一致一定不是最新版本則進(jìn)行服務(wù)器端拉取數(shù)據(jù)更新數(shù)據(jù)和版本號(hào)后再提交本地修改(如果有修改)。

    從服務(wù)器端拉取數(shù)據(jù)流程

    從服務(wù)器端拉取更新有些麻煩,如果在一臺(tái)設(shè)備上有幾個(gè)版本沒有更新的話,需要考慮將幾個(gè)版本的數(shù)據(jù)合并,具體問題以及流程在后文中會(huì)提及。

    從服務(wù)器端拉取數(shù)據(jù)基本流程如下:

    客戶端拉取數(shù)據(jù)后更新本地?cái)?shù)據(jù)流程

    客戶端調(diào)用 PullData 接口 從服務(wù)器拉取本地需要修改的數(shù)據(jù)同時(shí)每一條數(shù)據(jù)都對(duì)應(yīng)一個(gè)操作狀態(tài)來更新本地?cái)?shù)據(jù),從服務(wù)器端返回?cái)?shù)據(jù)的同時(shí)返回?cái)?shù)據(jù)對(duì)應(yīng)的操作狀態(tài),客戶端根據(jù)返回的操作狀態(tài)對(duì)數(shù)據(jù)進(jìn)行相應(yīng)的處理,返回?cái)?shù)據(jù)時(shí)也需要將最新數(shù)據(jù)的版本號(hào)也返回用以客戶端更新本地?cái)?shù)據(jù)版本。

    客戶端向服務(wù)器推送更新

    客戶端調(diào)用 PushData 接口向服務(wù)器端推送更新,將需要提交的修改提交到服務(wù)器端,服務(wù)器端返回客戶端每一個(gè)需要進(jìn)行修改的數(shù)據(jù)的操作狀態(tài),是否修改成功。

    服務(wù)器端更新數(shù)據(jù)

    客戶端向服務(wù)器端推送更新之后,服務(wù)器端需要進(jìn)行處理。首先需要判斷客戶端的版本是否是最新版本,如果不是最新則提示客戶端先更新本地?cái)?shù)據(jù)到最新版本再更新數(shù)據(jù),如果是最新的再向下處理。之后需要將客戶端的請(qǐng)求數(shù)據(jù)(一個(gè)json字符串)反序列化轉(zhuǎn)換為請(qǐng)求實(shí)體列表,如果轉(zhuǎn)換失敗則說明客戶端的請(qǐng)求數(shù)據(jù)是有問題的則不進(jìn)行處理,如果轉(zhuǎn)換成功再向下處理。然后遍歷請(qǐng)求實(shí)體列表,根據(jù)請(qǐng)求數(shù)據(jù)的操作類型進(jìn)行不同數(shù)據(jù)操作,每條數(shù)據(jù)操作完之后設(shè)置對(duì)應(yīng)的操作狀態(tài)。最后所有請(qǐng)求數(shù)據(jù)更新完成之后,新增一個(gè)版本,并將版本設(shè)置到響應(yīng)。

    被我踩到的那些坑

    Pull 數(shù)據(jù)版本合并

    從服務(wù)器端拉取數(shù)據(jù)的時(shí)候需要考慮到多個(gè)版本的提交數(shù)據(jù)合并問題,我們的數(shù)據(jù)比較簡(jiǎn)單是直接更新原來的數(shù)據(jù),因此不會(huì)涉及到文本分塊再合并這一類太復(fù)雜的操作,但是也需要將幾個(gè)版本的修改進(jìn)行合并,例如新增數(shù)據(jù),兩個(gè)版本各新增兩條數(shù)據(jù)則應(yīng)返回四條數(shù)據(jù)才對(duì),一個(gè)版本新增另一個(gè)版本刪除掉的數(shù)據(jù)就不應(yīng)該返回給客戶端。這就需要考慮如何高效并且準(zhǔn)確的返回客戶端需要更新的數(shù)據(jù),這里需要提及一下我的版本表的涉及,版本表里除了版本號(hào)之外有更新人,更新時(shí)間和每次調(diào)用 PushData 接口時(shí)的請(qǐng)求參數(shù)和返回給客戶端的操作狀態(tài)集合的響應(yīng)的轉(zhuǎn)換為json字符串存儲(chǔ)在數(shù)據(jù)庫中,每次更新完數(shù)據(jù)之后在版本表中插入一條新的版本數(shù)據(jù)。

    解決方案一:

    第一種方式,首先我考慮從版本表里取出每次修改成功的數(shù)據(jù),再將多個(gè)版本的修改進(jìn)行合并到一個(gè)List,再去重,如果遇到兩條相同的數(shù)據(jù)需要進(jìn)行去重操作,需要根據(jù)每條數(shù)據(jù)的操作類型來判斷該如何具體的去重,大致分四種情況:

  • 先新增后修改 --> Add

  • 先新增最后刪除 -->?null?不需要返回給客戶端

  • 先修改之后還是修改 --> Update

  • 先修改最后刪除 --> Delete

  • 這里不僅操作類型需要修改,數(shù)據(jù)內(nèi)容也是需要進(jìn)行合并的,需要最新的數(shù)據(jù)返回。

    解決方案二:

    第二種方式,按照版本的更新時(shí)間和數(shù)據(jù)的創(chuàng)建時(shí)間和更新時(shí)間的關(guān)系來進(jìn)行篩選數(shù)據(jù)和判斷數(shù)據(jù)的操作類型,如果數(shù)據(jù)刪除的話只是修改數(shù)據(jù)的狀態(tài)并不真正的刪除數(shù)據(jù)。

    首先將更新時(shí)間大于本地版本對(duì)應(yīng)的版本更新時(shí)間的數(shù)據(jù)查詢出來,這些數(shù)據(jù)是在本地版本更新之后的所有數(shù)據(jù), 之后篩選數(shù)據(jù),按操作類型可分四種情況:

  • 創(chuàng)建時(shí)間 >= 版本更新時(shí)間 && IsDeleted = 0 --> Add

  • 創(chuàng)建時(shí)間 >= 版本更新時(shí)間 && IsDeleted = 1 -->?null?先創(chuàng)建后刪除,不需要返回到客戶端

  • 創(chuàng)建時(shí)間 < 版本更新時(shí)間 && IsDeleted = 0 --> Update

  • 創(chuàng)建時(shí)間 < 版本更新時(shí)間 && IsDeleted = 1 --> Delete

  • 篩選并判斷操作類型之后將數(shù)據(jù)返回給客戶端

    綜合比較,確定版本合并方案

    經(jīng)過分析,第一種方案數(shù)據(jù)操作起來非常麻煩,相對(duì)的第二種解決方案數(shù)據(jù)操作會(huì)很少,可以在數(shù)據(jù)庫層面進(jìn)行判斷篩選,至于數(shù)據(jù)準(zhǔn)確度方面兩者差不多, 考慮并發(fā)問題的話可以在 調(diào)用 Push 接口時(shí)根據(jù)用戶賬號(hào)進(jìn)行加鎖,綜合一下,最終采用第二種解決方案。

    Push接口

    調(diào)用Push接口的時(shí)候原本沒有判斷本地的版本號(hào),如果出現(xiàn)客戶端沒有按照設(shè)定的順序來調(diào)用接口可能就會(huì)出現(xiàn)不可想象的數(shù)據(jù)災(zāi)難,而且作為接口本身是沒辦法控制客戶端的調(diào)用順序的。所以,修改后的 Push 接口需要客戶端傳遞一個(gè)客戶端版本號(hào)的參數(shù),如果不是最新版本的數(shù)據(jù)拒絕提交,并提示客戶端先更新數(shù)據(jù)到最新版本后再提交數(shù)據(jù)。

    時(shí)間不統(tǒng)一

    這個(gè)問題算是自己給自己挖的坑,在更新數(shù)據(jù)的時(shí)候時(shí)間取的都是網(wǎng)站服務(wù)器端時(shí)間,但是在新增版本的時(shí)候新增的參數(shù)里的更新時(shí)間用的卻是數(shù)據(jù)庫服務(wù)器的時(shí)間,由于數(shù)據(jù)庫服務(wù)器和網(wǎng)站服務(wù)器不在一臺(tái)服務(wù)器上, 數(shù)據(jù)庫服務(wù)器的時(shí)間比網(wǎng)站服務(wù)器上的時(shí)間慢了幾秒,這導(dǎo)致我在從服務(wù)器端拉取數(shù)據(jù)時(shí)出現(xiàn)有的數(shù)據(jù)沒有拉取出來的情況,后來debug從數(shù)據(jù)庫中查詢數(shù)據(jù)確實(shí)更新了而且版本也正確插入了,最后一一記錄每一條數(shù)據(jù)的更新時(shí)間和每個(gè)版本的更新時(shí)間, 這才發(fā)現(xiàn)時(shí)間有點(diǎn)不太對(duì),再檢查下自己的sql語句,發(fā)現(xiàn)新增版本的sql的更新時(shí)間用的是 GETDATE(),而更新數(shù)據(jù)的sql都是參數(shù),用的是網(wǎng)站服務(wù)器的時(shí)間。。發(fā)現(xiàn)問題的我頓時(shí)想抽死自己...(

    More

    上面主要解決了基本的數(shù)據(jù)增量同步的問題,但仍然存在一些問題??赡艽嬖诘闹饕獑栴}:

  • 大數(shù)據(jù)量傳輸時(shí),數(shù)據(jù)在傳輸過程出現(xiàn)部分丟失,數(shù)據(jù)不完整

  • 超大數(shù)據(jù)量需要同步,導(dǎo)致響應(yīng)時(shí)間過長(zhǎng)而導(dǎo)致連接超時(shí)

  • 針對(duì)以上可能出現(xiàn)的這兩個(gè)問題,需要對(duì)數(shù)據(jù)進(jìn)行校驗(yàn)并且數(shù)據(jù)量超過一定量時(shí)進(jìn)行分批量傳輸, 本文將著手解決 數(shù)據(jù)校驗(yàn)數(shù)據(jù)分批次傳輸 這兩個(gè)問題。

    同步流程概覽

    結(jié)合之前的同步流程,加上數(shù)據(jù)校驗(yàn)和分批次傳輸數(shù)據(jù),大概流程如下:客戶端調(diào)用服務(wù)器端的 Pull 接口從服務(wù)器端拉取數(shù)據(jù), 如果本地版本號(hào)等于服務(wù)器端最新版本號(hào),則已更新的最新版本, 如果本地版本小于服務(wù)器端最新的版本號(hào),則拉取需要更新的數(shù)據(jù),服務(wù)器端返回?cái)?shù)據(jù)的同時(shí)會(huì)返回本地傳輸?shù)臄?shù)據(jù)的一個(gè)校驗(yàn)值, 客戶端獲取到服務(wù)器端響應(yīng)時(shí)先根據(jù)接收到的數(shù)據(jù)計(jì)算校驗(yàn)值,計(jì)算出來之后與服務(wù)器端返回的校驗(yàn)值進(jìn)行比較, 如果本地計(jì)算的校驗(yàn)值與服務(wù)器端返回的校驗(yàn)值一致則進(jìn)行更新客戶端本地?cái)?shù)據(jù),不一致則視為無效數(shù)據(jù),重新請(qǐng)求 Pull 接口。

    更新到最新版本之后,判斷本地是否存在未提交的版本,如果本地不存在修改則本次數(shù)據(jù)同步完成,如果本地存在修改,則提交本地修改,提交本地?cái)?shù)據(jù)的之前要先計(jì)算傳輸數(shù)據(jù)的校驗(yàn)值,校驗(yàn)值和本地?cái)?shù)據(jù)一起傳給服務(wù)器端 Push接口。服務(wù)器端 Push 接收到客戶端請(qǐng)求之后需要進(jìn)行數(shù)據(jù)校驗(yàn),根據(jù)傳輸?shù)臄?shù)據(jù)計(jì)算校驗(yàn)值并與客戶端傳的校驗(yàn)值比較, 如果兩個(gè)值不一致,則視為數(shù)據(jù)在傳輸過程中發(fā)生丟失或是異常數(shù)據(jù),則不處理并返回客戶端,本次請(qǐng)求屬于異常請(qǐng)求。如果兩個(gè)值一致,再進(jìn)行數(shù)據(jù)處理,處理結(jié)束之后,數(shù)據(jù)會(huì)有一個(gè)返回狀態(tài)和其他必要的屬性,根據(jù)數(shù)據(jù)計(jì)算校驗(yàn)值,與從服務(wù)器拉取數(shù)據(jù)時(shí)類似,不再贅述, 客戶端數(shù)據(jù)校驗(yàn)通過之后,根據(jù)服務(wù)器端處理狀態(tài)進(jìn)行本地?cái)?shù)據(jù)的更新。

    下面展示添加數(shù)據(jù)校驗(yàn)后的主要流程圖:

    服務(wù)器端獲取數(shù)據(jù):

    客戶端拉取數(shù)據(jù):

    服務(wù)器端更新數(shù)據(jù):

    客戶端推送更新數(shù)據(jù):

    數(shù)據(jù)校驗(yàn)

    數(shù)據(jù)校驗(yàn),我們用的是MD5進(jìn)行校驗(yàn),取傳輸數(shù)據(jù)的MD5,使用MD5有兩方面的考慮:一方面因?yàn)镸D5生成的字符串不算太長(zhǎng),不會(huì)影響傳輸?shù)臄?shù)據(jù)量, 另一方面也是因?yàn)镸D5比較通用一些,生成效率相對(duì)SHA這些較高,APP端實(shí)現(xiàn)起來也比較方便。

    數(shù)據(jù)分批傳輸

    數(shù)據(jù)分批次傳輸,自己感覺這里實(shí)現(xiàn)的比較 LOW ,這里類似于網(wǎng)站上的分頁,沒想到更好的解決方案,期待大神分享更好的解決方案。返回客戶端 當(dāng)前請(qǐng)求數(shù)據(jù)頁碼索引 和 本次數(shù)據(jù)傳輸總頁數(shù),如果頁碼索引小于總頁數(shù),則頁碼索引+1,再請(qǐng)求一次接口知道返回的頁碼索引等于總頁數(shù)。

    End

    最后,這個(gè)設(shè)計(jì)一定還存在著不足,有不正確的地方還希望能夠告知。

    整個(gè)同步流程設(shè)計(jì)的流程圖,點(diǎn)我下載

    總結(jié)

    以上是生活随笔為你收集整理的多终端数据同步机制设计的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。