1. 程式人生 > >Twitter-Snowflake,64位自增ID演算法詳解

Twitter-Snowflake,64位自增ID演算法詳解

     這樣的好處是,整體上按照時間自增排序,並且整個分散式系統內不會產生ID碰撞(由datacenter和機器ID作區分),並且效率較高,經測試,snowflake每秒能夠產生26萬ID左右,完全滿足需要。

Snowflake演算法核心

時間戳工作機器id序列號組合在一起。

snowflake-64bit

除了最高位bit標記為不可用以外,其餘三組bit佔位均可浮動,看具體的業務需求而定。預設情況下41bit的時間戳可以支援該演算法使用到2082年,10bit的工作機器id可以支援1023臺機器,序列號支援1毫秒產生4095個自增序列id。下文會具體分析。

Snowflake – 時間戳

這裡時間戳的細度是毫秒級,具體程式碼如下,建議使用64位

Linux系統機器,因為有vdso,gettimeofday()在使用者態就可以完成操作,減少了進入核心態的損耗。

uint64_t generateStamp()  
{  
    timeval tv;  
    gettimeofday(&tv, 0);  
    return(uint64_t)tv.tv_sec * 1000 + (uint64_t)tv.tv_usec / 1000;  
}  

預設情況下有41個bit可以供使用,那麼一共有T(1llu << 41)毫秒供你使用分配,年份 = T / (3600 * 24 * 365 * 1000) = 69.7年。如果你只給時間戳分配39個bit使用,那麼根據同樣的演算法最後年份 = 17.4年。

Snowflake – 工作機器id

嚴格意義上來說這個bit段的使用可以是程序級,機器級的話你可以使用MAC地址來唯一標示工作機器工作程序級可以使用IP+Path來區分工作程序。如果工作機器比較少,可以使用配置檔案來設定這個id是一個不錯的選擇,如果機器過多配置檔案的維護是一個災難性的事情。

這裡的解決方案是需要一個工作id分配的程序,可以使用自己編寫一個簡單程序來記錄分配id,或者利用MySQL auto_increment機制也可以達到效果。

snowflake - 工作id

工作程序與工作id分配器只是在工作程序啟動的時候互動一次,然後工作程序可以自行將分配的id資料落檔案,下一次啟動直接讀取檔案裡的id使用。

PS:這個工作機器id的bit段也可以進一步拆分,比如用前5個bit標記程序id,後5個bit標記執行緒id之類:D

Snowflake – 序列號

序列號就是一系列的自增id(多執行緒建議使用atomic),為了處理在同一毫秒內需要給多條訊息分配id,若同一毫秒把序列號用完了,則“等待至下一毫秒”。

uint64_t waitNextMs(uint64_t lastStamp)  
{  
    uint64_t cur = 0;  
    do{  
        cur = generateStamp();  
    }while(cur <= lastStamp);  
    returncur;  
}  

      總體來說,是一個很高效很方便的GUID產生演算法,一個int64_t欄位就可以勝任,不像現在主流128bit的GUID演算法,即使無法保證嚴格的id序列性,但是對於特定的業務,比如用做遊戲伺服器端的GUID產生會很方便。另外,在多執行緒的環境下,序列號使用atomic可以在程式碼實現上有效減少鎖的密度。