因爲markdown的樣式太醜了,懶得再調整了,我另外再貼一個github的博客該文的 github連接git
最近在工做中遇到一個比較棘手的問題,客戶端從服務端同步數據的問題。
背景簡介:客戶端有N個,客戶端上的同步時間,各不相同。同步的時候,是一次獲取10條數據,多批次獲取。即分頁獲取。
在代碼中存在兩種同步的方式:github
惟一約束
的ID
來實現同步。只適用於數據量小的表,浪費網絡流量。大於
客戶端最新時間
的數據;依賴於時間戳
,問題時間戳不惟一
存在相同時間點下面多條數據,會出現數據遺漏,也會重複拉取數據,浪費網絡流量。本文的所使用到的解決辦法,就是結合了惟一ID和時間戳,兩個入參來作增量同步。本文也只作邏輯層面的說明。算法
表結構:ID 具備惟一約束, Name 姓名, UpdateTime 更新時間;如今問題的關鍵是ID爲3,4,這兩條時間點相同的數據。
假如一次只能同步一條數據,如何同步完ID 2後,再同步 ID 3。sql
ID | Name | UpdateTime |
---|---|---|
1 | 張三 | 2018-11-10 |
2 | 李四 | 2018-12-10 |
3 | 王五 | 2018-12-10 |
4 | 趙六 | 2018-11-20 |
5 | 金七 | 2018-11-30 |
經過 UpdateTime 和 ID 這兩種數據,經過某種運算,生成新的數。而這個新的數
具有可排序和惟一;同時還要攜帶有ID
和UpdateTime
的信息。
簡單表述就是,具備一個函數f: f(可排序A,可排序惟一B) = 可排序惟一C 。 C 的惟一解是 A和B。RSA加密算法數據庫
我想出了一個方法,也是生活中比較經常使用的方法:服務器
權重
必須大於ID
的可能最大值。如: 20181210 * 100 = 2018121000,Max(ID)<999ID
。如: 2018121000 + 3 = 2018121003。這個時候,2018121003 這個數,既包含了UpdateTime
和ID
的信息,又具備可排序和惟一性。用它做爲增量更新的判斷點,是再好不過的了。
可是它具備很大的缺點:數字太大了,時間轉化成數字,目前仍是用的是天級別,若是換成毫秒級別呢。還有ID可能的最大值也夠大了,若是是int64那就更沒得搞了。markdown
這個方法理論上可行,實際中不可用基本不可行,除非找到一種很是好的函數f;
PS: 個人直覺告訴我: 很可能存在這種函數,既知足個人須要,又能夠克服數字很大這個問題。只是我目前不知道。網絡
修改數據內容,使 UpdateTime
數據值惟一。缺點也比較明顯:框架
還有一種辦法,就是在數據庫中,增長一個新的字段,專門用來同步數據的時候使用。
比方說,增長字段 SyncData
int 類型。若是 UpdateTime 發生了改變,就把它更新爲 SyncData = Max(SyncData) + 1
;
也就是說, SyncData
這個字段的最大值必定是最新的數據,SyncData
的降序就是 更新時間的降序。SyncData
是更新時間順序
的充分沒必要要條件。函數
總的來講,這種辦法是比較好的,但缺點也比較明顯:
首先,先來分析一下,一次提取10條數據,提取的數據,存在的可能狀況。再次說明前提,先時間倒序,再ID倒序。Order By UpdateTime DESC, ID DESC
可能狀況以下圖,能夠簡化爲三種:
其中情景1
和情景3
,能夠把查詢條件變爲:WHERE UpdateTime > sync_time LIMIT 10
可是情景2
的狀況不能使用大於>
這個條件。假如使用了大於>
這個條件,情景2
就會變成情景1
或情景3
或圖3
這種狀況。不是包含部分了,須要額外特別處理。
注:圖3的結束點 ]
不重要,下面情景5有解釋。
提取的起始點:也就是說圖中[
左中括號的位置,須要準肯定位這個位置。
至於結束點:圖中]
右中括號的位置是在哪裏。這個就不重要了,由於下一次的分頁提取的起始點
,就是上一次的結束點。只須要關注起始點就足夠了。
而根據起始點,又能夠把情景2
,再作一次簡化:
針對情景4
。這個時候,時間戳sync_time
一個入參就不夠了,還額外須要惟一鍵ID來準肯定位。能夠把查詢寫做:WHERE UpdateTime = sync_time AND ID > sync_id LIMIT 10
。
若是查詢的行數 等於 10,則是圖4;小於 10,則是圖2,圖6,圖7的狀況。
針對情景5
。依舊可使用:WHERE UpdateTime > sync_time LIMIT 10
完整的分頁過程的步驟:
1、先用起始點來過濾:WHERE UpdateTime = sync_time AND ID > sync_id LIMIT 10
,查詢結果行數N。若是 N=10
是圖4的狀況,則結束,而且直接返回結果。若是 0<= N <10
,則進行第二步,其中N=0
是圖1,圖3,圖5,圖···的狀況;
2、再用時間戳查詢:WHERE UpdateTime > sync_time LIMIT 10-N
,查詢結果行數 M ,0<= M <=10-N
;這個階段,是否同一個時間點都不重要了。只須要按着順序
取已排序的數據就能夠了;
3、把一和二的結果集合並,一併返回。
4、重複步驟一二三,直到,分頁獲取的最後一條數據的ID
,是服務端數據庫中最新的ID;(防止存在,剛好這十條是所須要獲取的最後十條)。
服務端中最新ID獲取:Select Id From myTable Order by UpdateTime desc,ID desc Limit 1
;
尋找關鍵信息,以及具備指標意義的數據,或者數據的組合。
拆分問題,簡化問題
同步數據的可能性
,慢慢歸類,簡化後;才發現。問題沒有那麼難,僅僅是起始點這一個小小的問題。使用邏輯分析和哲學概括
惟一
和可排序
;跳出了具體字段,使用場景的框架束縛,而去考慮這兩種性質怎麼結合的問題;查詢的方法
;有點繞,打個比方,既要優化最終產品,也要去優化製做工藝;最後,我認爲我最近的邏輯分析能力,好像有比較大的提高。