简介
Snowflake(雪花)是Twitter
开源的高性能ID生成算法,它具有以下优缺点:
优点:
- 毫秒数在高位、自增序列在低位,整个ID都是趋势递增的
- 不依赖数据库等第三方系统、以服务方式部署、稳定性更高
- 可以根据自身业务特性分配bit位,较为灵活 缺点:
- 存在时钟回拨问题
- workid不易管理等问题
SnowFlake具体实现
snowflake
以64bit来存储组成ID的几个部分,如上图所示:
- 最高位占1bit,值固定为0,以保证生成的ID为正数(二进制中最高位为符号位,1表示负数,0表示正数)
- 时间41bit长度,使用毫秒级别精度,带有一个epoch,大概可以使用69年
- 10bit的可配置机器ID
- 这里可以拆分成5bit的Worker ID 和5bit Data Center ID(数据中心ID)
- 末位占12bit,值为当前毫秒内生成的不同ID,值得上限为2^12 = 4096
首先先定义一些常量用于实现snowflake的基础数据:
const (
twepoch = int64(1525705533000) // 开始时间截
workeridBits = uint(10) // 机器id所占的位数
sequenceBits = uint(12) // 序列所占的位数
workeridMax = int64(-1 ^ (-1 << workeridBits)) // 支持的最大机器id数量
sequenceMask = int64(-1 ^ (-1 << sequenceBits)) // 毫秒内存列的最大值
workeridShift = sequenceBits // 机器id左移位数
timestampShift = sequenceBits + workeridBits // 时间戳左移位数 22位
)
// 定义结构体struct
type SnowFlake struct{
sync.Mutex
timestamp int64
workerid int64
sequence int64
}
// 实例化snowflake
func NewSnowFlake(wokerid int64)(*SnowFlake,error){
// 判定workerid是否在合理范围内
if workerid < 0 || workerid > workeridMax{
return nil,errors.New("error happen")
}
return &Snowflake{
timestamp: 0,
workerid: workerid,
sequence: 0,
}, nil
}
snowflake生成ID的具体实现
func (s *Snowflake) Generate() int64 {
s.Lock()
defer s.Unlock()
now := time.Now().UnixNano() / 1000000
// 如果是微妙时间戳、且处于同一秒
if s.timestamp == now {
// 确保sequence不会溢出,最大值为4095、保证1ms内最多生成4096个ID值
s.sequence = (s.sequence + 1) & sequenceMask
if s.sequence == 0 {
// 如果当前时间小于等于当前时间戳
for now <= s.timestamp {
// 死循环等待下一个毫秒值
now = time.Now().UnixNano() / 1000000
}
}
} else {
s.sequence = 0
}
// todo: 时钟回拨判断、如果获取到的时间戳比上一个小,则存在时钟回拨状态,需要抛出异常
s.timestamp = now
// 时间戳左移22位 | workeid左移12位 | sequence兜底
r := (now-twepoch)<<timestampShift | (s.workerid << workeridShift) | (s.sequence)
return r
}
位运算
上面代码实现中有一些位运算相关实现。
sequence = (sequence + 1) & sequenceMask // 防止溢出
ntp导致的时钟回拨问题
服务器时间校准一般是通过ntp进程去校准的。但是由于校准这个动作,会导致时钟跳跃变化现象。
如何解决时钟回拨问题
- 关闭NTP服务
- 当时钟回拨过程直接抛出异常