在應用程序中,常常須要全局惟一的ID做爲數據庫主鍵。git
Twitter把存儲系統從MySQL遷移到Cassandra,由於Cassandra沒有順序ID生成機制,因此開發了這樣一套全局惟一ID生成服務。Ray的基本思想來自於SnowFlake,解決了一些SnowFlake中存在的一些問題github
最簡單的思路:時間戳+序列號 僞代碼:數據庫
If 當前時間 > 上次ID生成時間 -> 當前時間+序列號0
If 當前時間 = 上次ID生成時間 -> 當前時間 + 上次序列號+1
If 當前時間 < 上次ID生成時間 -> 是否存在這種狀況
複製代碼
如何判斷兩個ID是重複的?咱們認爲兩個ID每一位都是重複的則兩個ID重複。bash
兩個重複的數值若是分別拼接上不一樣的數值,則最終這兩個數值不相同,如:併發
1111和1111,分別拼上1和2分佈式
則1111-1和1111-2是不相同的性能
以前咱們在經過時間戳+序列號實現單機內不重複學習
那麼咱們只須要保證單機內不重複的ID + 不重複的實例ID(workId)就能保證最終生成的ID不重複spa
這是一個分佈式一致性問題設計
這個可使用分佈式鎖實現每一個實例獨佔一個workId
而且這僅在啓動時和續約時會依賴中間件
即便依賴的中間件中間暫時不可用,只是新的服務不能使用,舊的正常
現代計算機至少有兩種不一樣的時鐘:時鐘和單調鍾。儘管它們都衡量時間,但區分這二者很重要,由於它們有不一樣的目的。
它根據某個日曆返回當前日期和時間。例如: Java中的System.currentTimeMillis()返回自epoch(1970年1月1日 午夜 UTC,格里高利曆)以來的秒數(或毫秒),根據公曆日曆,不包括閏秒。
適用於測量持續時間(時間間隔),例如Java中的System.nanoTime()都是單調時鐘。這個名字來源於他們保證老是前進的事實。
時鐘的問題在於,雖然它們看起來簡單易用,但卻具備使人驚訝的缺陷:一天可能不會有精確的86,400秒,時鐘可能會先後跳躍,而一個節點上的時間可能與另外一個節點上的時間徹底不一樣。 若是時間往前撥咱們就沒法確保時間戳+序列號生成的ID是單機惟一的。
SnowFlake單位毫秒內生成的ID數不能超過12位的序列位
也就是說SnowFlake嚴重依賴於當前時間戳,而且只能處理當前時間戳大於等於上次時間戳的狀況,對於當前時間戳小於上次時間戳的狀況沒法處理。
If 當前時間 > 上次ID生成時間 -> 當前時間+序列號0
If 當前時間 = 上次ID生成時間 -> 當前時間 + 上次序列號+1
If 當前時間 < 上次ID生成時間 ->上次ID生成時間 + 上次序列號 +1
複製代碼
時鐘偏斜,時間前跳其實就是等價於當前時間 < 上次ID生成時間
首先上次ID生成時間記錄在當前實例內存中 若是時鐘回撥發生在重啓時,上次ID生成時間記錄會丟 此時新實例拿到了它的workId,而且新機器的時間戳更早就會出現ID重複的狀況 因此須要後臺按期同步全局最大的時間戳到中間件,實例退出和啓動前同步時間戳
SnowFlake爲何會出現序列號只有4096位的狀況? 1.位數就那麼多,你能夠從機器位上分配 2.沒有進位的空間,只支持當前時間戳>=上次時間戳 Ray支持當前時間<上次時間 意味着4096位用完能夠往時間戳上進位,解決突發流量
Ray參考了SnowFlake的設計,解決了時鐘回調和序列號不足的問題,而且提供了更好的併發性能 Github地址:github.com/KeshawnVan/…