分散式儲存中,生成全域性唯一ID的幾種方案
1.自定義生成規則 eg: 3位伺服器編碼+15位年月日時分秒毫秒+3位表編碼+4位隨機碼 (這樣就完全單機完成編碼任務)---共25位 3位伺服器編碼+15位年月日時分秒毫秒+3位表編碼+4流水碼 (這樣流水碼就需要結合資料庫和快取)---共25位 2.單獨開一個數據庫,獲取全域性唯一的自增序列或個表的MaxId eg: Flickr 方案 --- http://blog.csdn.net/taotao4/article/details/46520053 replace into + 奇偶2個數據庫主鍵生成伺服器(防止單點故障)
資料在分片時,典型的是分庫分表,就有一個全域性ID生成的問題。
單純的生成全域性ID並不是什麼難題,但是生成的ID通常要滿足分片的一些要求:
1 不能有單點故障。
2 以時間為序,或者ID裡包含時間。這樣一是可以少一個索引,二是冷熱資料容易分離。
3 可以控制ShardingId。比如某一個使用者的文章要放在同一個分片內,這樣查詢效率高,修改也容易。
4 不要太長,最好64bit。使用long比較好操作,如果是96bit,那就要各種移位相當的不方便,還有可能有些元件不能支援這麼大的ID。
一 twitter
twitter在把儲存系統從MySQL遷移到Cassandra的過程中由於Cassandra沒有順序ID生成機制,於是自己開發了一套全域性唯一ID生成服務:Snowflake。
1 41位的時間序列(精確到毫秒,41位的長度可以使用69年)
2 10位的機器標識(10位的長度最多支援部署1024個節點)
3 12位的計數順序號(12位的計數順序號支援每個節點每毫秒產生4096個ID序號) 最高位是符號位,始終為0。
優點:
原理
時間戳
這裡時間戳的細度是毫秒級,具體程式碼如下,建議使用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年。
工作機器ID
嚴格意義上來說這個bit段的使用可以是程序級,機器級的話你可以使用MAC地址來唯一標示工作機器,工作程序級可以使用IP+Path來區分工作程序。如果工作機器比較少,可以使用配置檔案來設定這個id是一個不錯的選擇,如果機器過多配置檔案的維護是一個災難性的事情。
這裡的解決方案是需要一個工作id分配的程序,可以使用自己編寫一個簡單程序來記錄分配id,或者利用Mysql auto_increment機制也可以達到效果。
工作程序與工作id分配器只是在工作程序啟動的時候互動一次,然後工作程序可以自行將分配的id資料落檔案,下一次啟動直接讀取檔案裡的id使用。
PS:這個工作機器id的bit段也可以進一步拆分,比如用前5個bit標記程序id,後5個bit標記執行緒id之類:D
序列號
序列號就是一系列的自增id(多執行緒建議使用atomic),為了處理在同一毫秒內需要給多條訊息分配id,若同一毫秒把序列號用完了,則“等待至下一毫秒”。
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可以在程式碼實現上有效減少鎖的密度。
最高位是符號位,始終為0。
優點:高效能,低延遲;獨立的應用;按時間有序。
缺點:需要獨立的開發和部署。
java 實現程式碼
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
public class IdWorker
{
private final long workerId;
private final static long twepoch
= 1288834974657L;
|