什么是雪花算法(snowflake)?
SnowFlake
算法是Twitter公司推出,用来在一个分布式的环境中,生成分布式自增且不重复id的算法。
该ID由一个64位二进制数组成,如上图。
- 第一位 bit 不用,因为二进制中,最高位为1的是负数,而ID是整数,所以不用为0
- 后面的41位 bit 是产生该ID的毫秒级时间戳,41位毫秒即 2^41/(1000*3600*24*365) ≈ 69,可以用69年
- 10bit 机器编号,即2^8=1024,最多可以部署在 1024 台机器上
- 12bit 序列号,2^12=4096,即同一毫秒内最多可以产生 4096 个序列号
算法实现的思路如上图
将 时间戳左移22位,机器编号左移12位,序列号在最右侧不需要移动,再将3者按位或运算 |,得到一个 64 位二进制数,再将其转换为10进制数,即所需的雪花 ID
( 位或运算 | 的逻辑是只要两者中有1个数字是1,则结果为1,只有两者都是0时,结果才是0 )
实现代码如下:
const (
timeLeft = 22 // 时间戳左移的位数
machineLeft = 12 // 机器序号左移的位数
maxWorkID = 4095 // 每一毫秒内最大的工作id
maxMachineID = 1023 // 最大的机器编号
// 开始时间,2023-01-01 00:00:00的毫秒时间戳, 运行一段时间后如果修改该值可能导致生成重复id
startTime = 1672502400000 //
)
type snowflake struct {
seq int64 // 步进号,每生成1个id自增1
timeStamp int64 // 当前生成id的时间戳
machineId int64 // 机器编号,范围只能在 [0:1024)
mu sync.Mutex
}
// NewSnowflake 输入机器编号,创建新的雪花算法
func NewSnowflake(id int64) (*snowflake, error) {
if id < 0 || id > maxMachineID {
return nil, fmt.Errorf("illegal machineID, the range should be between 0 and 1023")
}
return &snowflake{
//timeStamp: getNowLeftTime(),
timeStamp: time.Now().UnixNano() / 1e6,
machineId: id,
}, nil
}
// GetID 获取新的雪花id
func (s *snowflake) GetID() int64 {
s.mu.Lock()
defer s.mu.Unlock()
var id int64
curTime := time.Now().UnixNano() / 1e6
// 新生成的雪花id时间戳必须大于等于当前时间的时间戳
if s.timeStamp < curTime {
s.timeStamp = curTime
s.seq = 0
}
// 当前时间戳生成的雪花id已经达到最大数值后,必须进入下一毫秒才能继续生成
if s.seq > maxWorkID {
time.Sleep(time.Millisecond)
s.timeStamp = time.Now().UnixNano() / 1e6
s.seq = 0
}
id = ((s.timeStamp - startTime) << timeLeft) | (s.machineId << machineLeft) | s.seq
s.seq++
return id
}
// 测试
func main() {
s, _ := NewSnowflake(1)
ch := make(chan int64, 1000)
for i := 0; i < 100; i++ {
go func() {
for {
ch <- s.GetID()
}
}()
}
i := 0
for v := range ch {
i++
fmt.Println(v) // 输出的结果均为有序
if i == 1000 {
close(ch)
return
}
}
}