位置 | 大小 | 作用 |
---|---|---|
0~11bit | 12bits | 序列號,用來對同一個毫秒之內產生不同的ID,可記錄4095個 |
12~21bit | 10bits | 10bit用來記錄機器ID,總共可以記錄1024臺機器 |
22~62bit | 41bits | 用來記錄時間戳,這里可以記錄69年 |
63bit | 1bit | 符號位,不做處理 |
上面只是一個將 64bit 劃分的通用標準,一般的情況可以根據自己的業務情況進行調整。例如目前業務只有機器10臺左右預計未來會增加到三位數,并且需要進行多機房部署,QPS 幾年之內會發展到百萬。
那么對于百萬 QPS 平分到 10 臺機器上就是每臺機器承擔十萬級的請求即可,12 bit 的序列號完全夠用。對于未來會增加到三位數機器,并且需要多機房部署的需求我們僅需要將 10 bits 的 work id 進行拆分,分割 3 bits 來代表機房數共代表可以部署8個機房,其他 7bits 代表機器數代表每個機房可以部署128臺機器。那么數據格式就會如下所示:
其實看懂了上面的數據結構之后,需要自己實現一個雪花算法是非常簡單,步驟大致如下:
首先我們需要定義一個 Snowflake 結構體:
type Snowflake struct { sync.Mutex // 鎖 timestamp int64 // 時間戳 ,毫秒 workerid int64 // 工作節點 datacenterid int64 // 數據中心機房id sequence int64 // 序列號 }
然后我們需要定義一些常量,方便我們在使用雪花算法的時候進行位運算取值:
const ( epoch = int64(1577808000000) // 設置起始時間(時間戳/毫秒):2020-01-01 00:00:00,有效期69年 timestampBits = uint(41) // 時間戳占用位數 datacenteridBits = uint(2) // 數據中心id所占位數 workeridBits = uint(7) // 機器id所占位數 sequenceBits = uint(12) // 序列所占的位數 timestampMax = int64(-1 ^ (-1 timestampBits)) // 時間戳最大值 datacenteridMax = int64(-1 ^ (-1 datacenteridBits)) // 支持的最大數據中心id數量 workeridMax = int64(-1 ^ (-1 workeridBits)) // 支持的最大機器id數量 sequenceMask = int64(-1 ^ (-1 sequenceBits)) // 支持的最大序列id數量 workeridShift = sequenceBits // 機器id左移位數 datacenteridShift = sequenceBits + workeridBits // 數據中心id左移位數 timestampShift = sequenceBits + workeridBits + datacenteridBits // 時間戳左移位數 )
需要注意的是由于 -1 在二進制上表示是:
11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
所以想要求得 41bits 的 timestamp 最大值可以將 -1 向左位移 41 位,得到:
11111111 11111111 11111110 00000000 00000000 00000000 00000000 00000000
那么再和 -1 進行 ^異或運算:
00000000 00000000 00000001 11111111 11111111 11111111 11111111 11111111
這就可以表示 41bits 的 timestamp 最大值。datacenteridMax、workeridMax也同理。
接著我們就可以設置一個 NextVal 函數來獲取 Snowflake 返回的 ID 了:
func (s *Snowflake) NextVal() int64 { s.Lock() now := time.Now().UnixNano() / 1000000 // 轉毫秒 if s.timestamp == now { // 當同一時間戳(精度:毫秒)下多次生成id會增加序列號 s.sequence = (s.sequence + 1) sequenceMask if s.sequence == 0 { // 如果當前序列超出12bit長度,則需要等待下一毫秒 // 下一毫秒將使用sequence:0 for now = s.timestamp { now = time.Now().UnixNano() / 1000000 } } } else { // 不同時間戳(精度:毫秒)下直接使用序列號:0 s.sequence = 0 } t := now - epoch if t > timestampMax { s.Unlock() glog.Errorf("epoch must be between 0 and %d", timestampMax-1) return 0 } s.timestamp = now r := int64((t)timestampShift | (s.datacenterid datacenteridShift) | (s.workerid workeridShift) | (s.sequence)) s.Unlock() return r }
上面的代碼也是非常的簡單,看看注釋應該也能懂,我這里說說最后返回的 r 系列的位運算表示什么意思。
首先 t 表示的是現在距離 epoch 的時間差,我們 epoch 在初始化的時候設置的是2020-01-01 00:00:00,那么對于 41bit 的 timestamp 來說會在 69 年之后才溢出。對 t 進行向左位移之后,低于 timestampShift 位置上全是0 ,由 datacenterid、workerid、sequence 進行取或填充。
在當前的例子中,如果當前時間是2021/01/01 00:00:00,那么位運算結果如下圖所示:
到此這篇關于Go語言實現Snowflake雪花算法的文章就介紹到這了,更多相關Go語言雪花算法內容請搜索腳本之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持腳本之家!