雪花演算法
Twitter 的
SnowFlake
是一種經典分散式 ID 生成演算法。ID 是一個 64 位的長整型。
1位
,不用。二進位制中最高位為1的都是負數,但是我們生成的id一般都使用整數,所以這個最高位固定是041位
,用來記錄時間戳(毫秒)。
41位可以表示241−1個數字,
- 如果只用來表示正整數(計算機中正數包含0),可以表示的數值範圍是:0 至 241−1,減1是因為可表示的數值範圍是從0開始算的,而不是1。\
也就是說41位可以表示241−1個毫秒的值,轉化成單位年則是(241−1)/(1000∗60∗60∗24∗365)=69年
10位
,用來記錄工作機器id。
可以部署在210=1024個節點,包括5位datacenterId和5位workerId\
5位(bit)可以表示的最大正整數是25−1=31,即可以用0、1、2、3、....31這32個數字,來表示不同的datecenterId或workerId
12位
,序列號,用來記錄同毫秒內產生的不同id。
12位(bit)可以表示的最大正整數是212−1=4095,即可以用0、1、2、3、....4094這4095個數字,來表示同一機器同一時間截(毫秒)內產生的4095個ID序號
// Fetch prints the content found at a URL.
package main
import (
"fmt"
"log"
"sync"
"time"
)
const (
workerIdBits int64 = 5
datacenterIdBits int64 = 5
sequenceBits int64 = 12
maxWorkerId int64 = -1 ^ (-1 << uint64(workerIdBits))
maxDatacenterId int64 = -1 ^ (-1 << uint64(datacenterIdBits))
maxSequence int64 = -1 ^ (-1 << uint64(sequenceBits))
timeLeft uint8 = 22
dataLeft uint8 = 17
workLeft uint8 = 12
twepoch int64 = 1525705533000
)
type worker struct {
mu sync.Mutex
laststamp int64
workerid int64
datacenterid int64
sequence int64
}
func(w *worker) getCurrentTime() int64 {
return time.Now().UnixNano() / 1e6
}
//var i int = 1
func(w *worker) nextId() int64 {
w.mu.Lock()
defer w.mu.Unlock()
timestamp := w.getCurrentTime()
if timestamp < w.laststamp {
log.Fatal("can not generate id")
}
if w.laststamp == timestamp {
// 這其實和 <==>
// w.sequence++
// if w.sequence++ > maxSequence 等價
w.sequence = (w.sequence + 1) & maxSequence
if w.sequence == 0 {
// 之前使用 if, 只是沒想到 GO 可以在一毫秒以內能生成到最大的 Sequence, 那樣就會導致很多重複的
// 這個地方使用 for 來等待下一毫秒
for timestamp <= w.laststamp {
//i++
//fmt.Println(i)
timestamp = w.getCurrentTime()
}
}
} else {
w.sequence = 0
}
w.laststamp = timestamp
return ((timestamp - twepoch) << timeLeft) | (w.datacenterid << dataLeft) | (w.workerid << workLeft) | w.sequence
}
func (w *worker) tilNextMillis() int64 {
timestamp := w.getCurrentTime()
if (timestamp <= w.laststamp) {
timestamp = w.getCurrentTime()
}
return timestamp
}
func main() {
w := new(worker)
// 上一次時間
w.laststamp = -1
w.workerid = 10
w.datacenterid = 12
w.sequence = 14
i := 0
r := make([]int64, 0)
for {
id := w.nextId()
r = append(r, id)
i++
if i > 10000000 {
break
}
}
j := 0
for _,v := range r {
if v > 1 {}
j++
}
fmt.Println(j)
fmt.Println(len(unique(r)))
fmt.Println(w)
}
func unique(m[]int64) []int64 {
s := make([]int64, 0)
smap := make(map[int64]int64)
for _, value := range m {
//計算map長度
length := len(smap)
smap[value] = 1
//比較map長度, 如果map長度不相等, 說明key不存在
if len(smap) != length {
s = append(s, value)
}
}
return s
}
程式碼解釋
maxSequence int64 = -1 ^ (-1 << uint64(sequenceBits))
負數的二進位制
這設計兩個名詞,反碼和補碼。來看一下整型 1
的二進位制原碼。
00000000 00000000 00000000 00000001
反碼 (二進位制取反)
11111111 11111111 11111111 11111110
補碼 (反碼 + 1)
11111111 11111111 11111111 11111111
再看這段程式碼,轉成二進位制:
11111111 11111111 11111111 11111111 ^ (11111111 11111111 11111111 11111111 << 12)\
11111111 11111111 11111111 11111111 ^ 11111111 11111111 11110000 00000000\
00000000 00000000 00001111 11111111 => 4095
整個過程大概就是這樣的\
還有就是 按位或 (|) 和 按位異或(^)的區別
0011 | 1011 => 1011 (按照征程 || 來想就可以了,1 true 0 false)\
0011 ^ 1011 => 1000 (位相同為零, 位不同為一)
最後從結果來看,生成 1000W,也沒有重複的。 而且速度也很快,個人感知大概在兩秒左右。