雪花算法源码解析
id的组成
雪花算法的id由64位组成,总共分为4部分,第一部分只有一位,是正负标识位,没有其他作用,第二部分由41位的时间戳,第三部分为工作机器id,第四部分为自增序列号
41位时间戳能表示毫秒级的时间为69年,意味着id在69年不重复(时钟不会滚)
10位工作机器id,这个10位可以自由调整,可以用来表示业务
12位自增序列号,表示同一毫秒内能生成的id的数量
源码解析
const (
nodeIDBits = uint64(10)
sequenceBits = uint64(12)
nodeIDShift = sequenceBits
timestampShift = sequenceBits + nodeIDBits
//11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 = -1
//11111111 11111111 11111111 11111111 11111111 11111111 11110000 00000000 = -1 << 12
//00000000 00000000 00000000 00000000 00000000 00000000 00001111 11111111 = -1 ^ (-1 << 12) 4095
sequenceMask = int64(-1) ^ (int64(-1) << sequenceBits)
// ( 2012-10-28 16:23:42 UTC ).UnixNano() >> 20
// 项目上线的时间
twepoch = int64(1288834974288)
)
type guid int64
type guidFactory struct {
sync.Mutex
nodeID int64
sequence int64
lastTimestamp int64
lastID guid
}
func (f *guidFactory) NewGUID() (guid, error) {
f.Lock()
// divide by 1048576, giving pseudo-milliseconds
// 伪毫秒,当前的时间戳
ts := time.Now().UnixNano() >> 20
// 时间小于上一次的时间,时钟回摆,返回异常,否则可能有重复的id
if ts < f.lastTimestamp {
f.Unlock()
return 0, ErrTimeBackwards
}
// 相同的时间毫秒数,则使用最后的12位自增序列号
if f.lastTimestamp == ts {
// 计算新的序列号
f.sequence = (f.sequence + 1) & sequenceMask
// 序列号==0 说明 sequence=4095 超出最大范围
if f.sequence == 0 {
f.Unlock()
return 0, ErrSequenceExpired
}
} else {
// 不是相同的毫秒,则自增数为0
f.sequence = 0
}
//赋值上次的时间毫秒数
f.lastTimestamp = ts
//计算id
id := guid(((ts - twepoch) << timestampShift) |
(f.nodeID << nodeIDShift) |
f.sequence)
// 校验id的合法性
if id <= f.lastID {
f.Unlock()
return 0, ErrIDBackwards
}
//更新上一个id
f.lastID = id
f.Unlock()
return id, nil
}
通过位运算的方式,高效率的将64位的整形 拆分成几个部分,最后再将几个部分合并,得到最终的数。
基础知识
二进制 负数表示
负数为正数的补码,补码是反码+1,反码为原码取反
-1
1 : 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
1的反码:11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111110
1的补码:11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
异或运算(^)
两个位相同为0,相异为1
0 0 1 0 0 0 1 1
0 0 1 0 1 1 1 1
-----------------
0 0 0 0 1 1 0 0
或运算(|)
两个位都为0,结果为0
0 0 1 0 0 0 1 1
0 0 1 0 1 1 1 1
-----------------
0 0 1 0 1 1 1 1
与运算(&)
两个位都为1,结果为1
0 0 1 0 0 0 1 1
0 0 1 0 1 1 1 1
-----------------
0 0 1 0 0 0 1 1
左移 (<<)
各二进位全部左移若干位,高位丢弃,低位补0
1010 1110 << 2 = 1011 1000
右移(>>)
各二进位全部右移若干位,对无符号数,高位补0,有符号数,各编译器处理方法不一样,有的补符号位(算术右移),有的补0(逻辑右移)
//无符号
1010 1110 >> 2 = 0010 1110
//有符号负数
1010 1110 >> 2 = 1110 1011