在一次公開課上,聽別人講過全局分散式uuid的設計,聽過twitter的snowflake的設計。也聽過,如果使用單獨的計數器服務,不可能每次都保存當前計數器到文本,自己想到應該可以每隔一些數,例如1萬次,10萬次,反正64位的空間比較大,然後保存起來,那麼就沒有每次保存,對硬碟的寫入壓力。當出現故 ...
在一次公開課上,聽別人講過全局分散式uuid的設計,聽過twitter的snowflake的設計。也聽過,如果使用單獨的計數器服務,不可能每次都保存當前計數器到文本,自己想到應該可以每隔一些數,例如1萬次,10萬次,反正64位的空間比較大,然後保存起來,那麼就沒有每次保存,對硬碟的寫入壓力。當出現故障的時候,跳過這麼多數就可以了。只是,這個據說也是微信的uuid設計方案。我如果拿這個來講,豈不是抄襲很嚴重。下麵先給出uuid服務的圖:
對於snowflake和微信的計數保存方案,各個各的優點,在我看來,可以考慮聯合這兩種方案。方案大致如下:
1. 對於不同的uuid server,給予不同的首碼,那麼不同的uuid伺服器的計數互不幹擾,這個依賴於運維來實現,當然,如果開發打算自己做,我覺得也沒有問題。
2. 對於每個uuid伺服器,啟動的時候,從當前電腦獲取出當前的時間,在這裡,可以考慮從epoch開始的秒數,然後有一個次數計數器,從0開始計數,假設這個計數器為32位,每次來一個請求,次數計數器+1,可以考慮當這個計數器達到最大值時,檢查一下當前秒數,是不是和上一次獲取秒數不同,如果相同(對於當前例子來說,基本不可能),則休眠若幹毫秒,然後再次獲取當前時間,如果不同,則將次數計數器重新置為0,繼續進行計數。你們應該不難看出,我這裡的計數器是
uuid server 的首碼 + 當前秒數 + 次數計數器值
3. 對於程式崩潰的處理,並不困難,只需要在程式啟動的地方添加一個sleep(休眠),讓程式1s後啟動就可以了,那麼就可以保證新的計數器,之前不會出現過。
4. 參數說明:上面所說的參數,可能不是最優的,例如,可以考慮,將時間改為從epoch開始的毫秒數,0.1s,或者其他的,這個看各自的需求,計數器,也可以考慮使用30個bit,24個bit,那麼對應的最大值也需要根據這個進行調整,可能使總的計數器位數更少,或者更多。總之,這些東西有賴於各自測試,達到自己的目的就好。調整時間的精度,例如精度調整為0.1s,那麼程式再次啟動,sleep的時間可以更短,這個對某些項目可能有所幫助。
5. 需要處理的問題,這裡主要是時間。有一點需要說明,這種實現方式,不同電腦存儲的uuid之間沒有嚴格的時間關係,沒辦法比較先後,同一臺電腦的uuid可以區分先後。對於同一臺電腦,重啟之後運行正常,依賴於本地時間沒有被改動,如果本地時間向前調整,那麼可能會出現uuid重覆的問題,先後順序也不能被保證。只是,如果次數計數器值空間很大,那麼出現重覆的幾率應該不大。如果打算將當前的首碼給另外一臺電腦使用,那麼需要那臺電腦的時間不低於當前電腦,否則也可能出現重覆。只是,如果那臺電腦的時間在這臺電腦時間之後,應該沒有問題。
6. 如果擔心時間問題,可以在程式中改變時間的時候,進行一次列印,列印出當前的時間,再次啟動的時候可以進行查看。
7. 順序的額外說明:不同電腦的時間同步對uuid跨電腦排序用處不大,因為uuid中的時間,只能說明記錄的uuid發生在這個時間或者之後,並不一定是這個時間。
8. 對於計數器的實現來說,可以考慮使用atomic,也可以考慮使用中間件一類的,看實現方便,以及各自喜好。
9. 如果次數計數器為64位,或者類似的長度,可以考慮不進行檢查,只在每次重啟的時候,sleep例如1s,然後從0開始計數,因為按照當前甚至未來一段時間,64位的數字也很難在比較短的時間內耗盡,這樣可以使處理邏輯更加簡單,不過,可能會增加總的計數器的位數.
關於這個問題,就簡單說到這裡,如果文中有什麼問題,希望能夠指出來。