Intro
由於項目須要,須要設計一個多終端數據同步的機制, 須要知足如下條件: 1. 多個終端數據操做及同步 2. 每次同步的時候只拉取須要同步的數據,且數據不能存在丟失 3. 儘量少的調用服務器端接口sql
同步流程
我想仿照Git數據同步的方式來進行數據同步,因而放着Git同步的流程來進行設計,首先每次提交會有一個版本號,另外每次提交以前應儘量先從服務器端拉取數據, 保證客戶端的數據是最新的狀況下再進行提交本地的修改。按照Git的方式來進行數據同步時,可能會存在數據衝突,若是存在數據衝突須要客戶端解決衝突。
也就是整體來講,操做有兩個大的操做,一個是從服務器端拉取數據,一個是向服務器端推送數據更新。
在數據庫層面有一個數據版本表來存儲每一次提交,每一次更新會在更新結束以後將在版本表中加上一條記錄,更新一個版本,並將版本號返回給客戶端, 每次從服務器端拉取更新的時候不只會將更新的數據返回給客戶端,也會將最新的版本號返回到客戶端,用以客戶端下一次同步數據。數據庫
最後服務器端提供了三個接口json
GetCurrentVersion()
查詢用戶數據的最新版本號,PullData()
從服務器端拉取更新數據,PushData()
向服務器端推送本地數據更新思慮再三以後最終產出了下面的流程圖: 服務器
客戶端調用 GetCurrentVersion()
接口,須要傳遞一個標識用戶帳號的參數,這樣才能查詢到某一個用戶的數據信息。 根據用戶帳號信息查詢數據的最新版本號,返回到客戶端,客戶端根據服務器端的版本號和本地進行比較,若是一致則說明是最新版本以後判斷本地是否有修改有修改則直接提交便可,若是不一致必定不是最新版本則進行服務器端拉取數據更新數據和版本號後再提交本地修改(若是有修改)。併發
從服務器端拉取更新有些麻煩,若是在一臺設備上有幾個版本沒有更新的話,須要考慮將幾個版本的數據合併,具體問題以及流程在後文中會說起。網站
從服務器端拉取數據基本流程以下:debug
客戶端調用 PullData
接口 從服務器拉取本地須要修改的數據同時每一條數據都對應一個操做狀態來更新本地數據,從服務器端返回數據的同時返回數據對應的操做狀態,客戶端根據返回的操做狀態對數據進行相應的處理,返回數據時也須要將最新數據的版本號也返回用以客戶端更新本地數據版本。設計
客戶端調用 PushData
接口向服務器端推送更新,將須要提交的修改提交到服務器端,服務器端返回客戶端每個須要進行修改的數據的操做狀態,是否修改爲功。code
客戶端向服務器端推送更新以後,服務器端須要進行處理。 首先須要判斷客戶端的版本是不是最新版本,若是不是最新則提示客戶端先更新本地數據到最新版本再更新數據,若是是最新的再向下處理。 以後須要將客戶端的請求數據(一個json字符串)反序列化轉換爲請求實體列表,若是轉換失敗則說明客戶端的請求數據是有問題的則不進行處理,若是轉換成功再向下處理。 而後遍歷請求實體列表,根據請求數據的操做類型進行不一樣數據操做,每條數據操做完以後設置對應的操做狀態。 最後全部請求數據更新完成以後,新增一個版本,並將版本設置到響應。blog
被我踩到的那些坑
從服務器端拉取數據的時候須要考慮到多個版本的提交數據合併問題,咱們的數據比較簡單是直接更新原來的數據,所以不會涉及到文本分塊再合併這一類太複雜的操做,可是也須要將幾個版本的修改進行合併,例如新增數據,兩個版本各新增兩條數據則應返回四條數據纔對,一個版本新增另外一個版本刪除掉的數據就不該該返回給客戶端。 這就須要考慮如何高效而且準確的返回客戶端須要更新的數據,這裏須要說起一下個人版本表的涉及,版本表裏除了版本號以外有更新人,更新時間和每次調用 PushData
接口時的請求參數和返回給客戶端的操做狀態集合的響應的轉換爲json字符串存儲在數據庫中,每次更新完數據以後在版本表中插入一條新的版本數據。
第一種方式,首先我考慮從版本表裏取出每次修改爲功的數據,再將多個版本的修改進行合併到一個List,再去重,若是遇到兩條相同的數據須要進行去重操做,須要根據每條數據的操做類型來判斷該如何具體的去重,大體分四種狀況:
null
不須要返回給客戶端這裏不只操做類型須要修改,數據內容也是須要進行合併的,須要最新的數據返回。
第二種方式,按照版本的更新時間和數據的建立時間和更新時間的關係來進行篩選數據和判斷數據的操做類型,若是數據刪除的話只是修改數據的狀態並不真正的刪除數據。
首先將更新時間大於本地版本對應的版本更新時間的數據查詢出來,這些數據是在本地版本更新以後的全部數據, 以後篩選數據,按操做類型可分四種狀況:
null
先建立後刪除,不須要返回到客戶端篩選並判斷操做類型以後將數據返回給客戶端
通過分析,第一種方案數據操做起來很是麻煩,相對的第二種解決方案數據操做會不多,能夠在數據庫層面進行判斷篩選,至於數據準確度方面二者差很少, 考慮併發問題的話能夠在 調用 Push 接口時根據用戶帳號進行加鎖,綜合一下,最終採用第二種解決方案。
調用Push接口的時候本來沒有判斷本地的版本號,若是出現客戶端沒有按照設定的順序來調用接口可能就會出現不可想象的數據災難,並且做爲接口自己是沒辦法控制客戶端的調用順序的。 因此,修改後的 Push 接口須要客戶端傳遞一個客戶端版本號的參數,若是不是最新版本的數據拒絕提交,並提示客戶端先更新數據到最新版本後再提交數據。
這個問題算是本身給本身挖的坑,在更新數據的時候時間取的都是網站服務器端時間,可是在新增版本的時候新增的參數裏的更新時間用的倒是數據庫服務器的時間,因爲數據庫服務器和網站服務器不在一臺服務器上, 數據庫服務器的時間比網站服務器上的時間慢了幾秒,這致使我在從服務器端拉取數據時出現有的數據沒有拉取出來的狀況,後來debug從數據庫中查詢數據確實更新了並且版本也正確插入了,最後一一記錄每一條數據的更新時間和每一個版本的更新時間, 這才發現時間有點不太對,再檢查下本身的sql語句,發現新增版本的sql的更新時間用的是GETDATE()
,而更新數據的sql都是參數,用的是網站服務器的時間。。發現問題的我頓時想抽死本身...(
最後,這個設計必定還存在着不足,給本身挖個坑,過一段時間再來填,【數據校驗】+【數據分割】,下一次解決這兩個問題,
但願大神看到能給出本身的見解和意見,有不正確的地方還但願可以告知。