Twitter-Snowflake算法產生的背景至關簡單,爲了知足Twitter每秒上萬條消息的請求,每條消息都必須分配一條惟一的id,這些id還須要一些大體的順序(方便客戶端排序),而且在分佈式系統中不一樣機器產生的id必須不一樣。html
把時間戳,工做機器id,序列號組合在一塊兒。linux
除了最高位bit標記爲不可用之外,其他三組bit佔位都可浮動,看具體的業務需求而定。默認狀況下41bit的時間戳能夠支持該算法使用到2082年,10bit的工做機器id能夠支持1023臺機器,序列號支持1毫秒產生4095個自增序列id。下文會具體分析。git
這裏時間戳的細度是毫秒級,具體代碼以下,建議使用64位linux系統機器,由於有vdso,gettimeofday()在用戶態就能夠完成操做,減小了進入內核態的損耗。github
1
2
3
4
5
6
|
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年。算法
嚴格意義上來講這個bit段的使用能夠是進程級,機器級的話你可使用MAC地址來惟一標示工做機器,工做進程級可使用IP+Path來區分工做進程。若是工做機器比較少,可使用配置文件來設置這個id是一個不錯的選擇,若是機器過多配置文件的維護是一個災難性的事情。sql
這裏的解決方案是須要一個工做id分配的進程,可使用本身編寫一個簡單進程來記錄分配id,或者利用Mysql auto_increment機制也能夠達到效果。服務器
工做進程與工做id分配器只是在工做進程啓動的時候交互一次,而後工做進程能夠自行將分配的id數據落文件,下一次啓動直接讀取文件裏的id使用。多線程
PS:這個工做機器id的bit段也能夠進一步拆分,好比用前5個bit標記進程id,後5個bit標記線程id之類:D分佈式
序列號就是一系列的自增id(多線程建議使用atomic),爲了處理在同一毫秒內須要給多條消息分配id,若同一毫秒把序列號用完了,則「等待至下一毫秒」。ui
1
2
3
4
5
6
7
8
|
uint64_t waitNextMs(uint64_t lastStamp)
{
uint64_t cur = 0;
do
{
cur = generateStamp();
}
while
(cur <= lastStamp);
return
cur;
}
|
整體來講,是一個很高效很方便的GUID產生算法,一個int64_t字段就能夠勝任,不像如今主流128bit的GUID算法,即便沒法保證嚴格的id序列性,可是對於特定的業務,好比用作遊戲服務器端的GUID產生會很方便。另外,在多線程的環境下,序列號使用atomic能夠在代碼實現上有效減小鎖的密度。
參考資料:https://github.com/twitter/snowflake
(全文結束)
原文連接:http://www.lanindex.com/twitter-snowflake%EF%BC%8C64%E4%BD%8D%E8%87%AA%E5%A2%9Eid%E7%AE%97%E6%B3%95%E8%AF%A6%E8%A7%A3/