自定义雪花算法(53位)避免超过9007199254740991最大值,超过前端JS精度
标准雪花算法是64位:0 | 41位时间戳 | 10位机器 | 12位序列 , 41时间戳只能支持69年,但是因为生成的值超过 9007199254740991(前端JavaScript最大精度),避免返回给前端,若前端未bigint处置造成错误的值,自定义53位。 当然还有种解决办法:转string后返回前端,弊端:每次麻烦
自定义雪花53位: ---可用方案1:0 | 41位时间戳(毫秒) | 5位机器ID | 7位序列号 最大机器ID=31,同1毫秒时可用最大序列号:127,可用:69年 ---可用方案2:0 | 41位时间戳(毫秒) | 3位机器ID | 9位序列号 最大机器ID=7,同1毫秒时可用最大序列号:511,可用:69年 ---可用方案3:0 | 42位时间戳(毫秒) | 3位机器ID | 8位序列号 最大机器ID=7,同1毫秒时可用最大序列号:255,可用:139年 ---可用方案4:0 | 43位时间戳(毫秒) | 3位机器ID | 7位序列号 最大机器ID=7,同1毫秒时可用最大序列号:127,可用:278年
生成效率: (8核) 500-1000万ID/秒的生成速率 本方法支持年份:采用方案2支持 69年 + 基础年 = 69+2024=2093年 ,之后时间戳会溢出,导致ID重复。
// 标准雪花算法是64位:0 | 41位时间戳 | 10位机器 | 12位序列 , 41时间戳只能支持69年
// 但是因为生成的值超过 9007199254740991(前端JavaScript最大精度),避免返回给前端,若前端未bigint处置造成错误的值,自定义53位。 当然还有种解决办法:转string后返回前端,弊端:每次麻烦
// 自定义雪花53位:
// ---可用方案1:0 | 41位时间戳(毫秒) | 5位机器ID | 7位序列号 最大机器ID=31,同1毫秒时可用最大序列号:127,可用:69年
// ---可用方案2:0 | 41位时间戳(毫秒) | 3位机器ID | 9位序列号 最大机器ID=7,同1毫秒时可用最大序列号:511,可用:69年
// ---可用方案3:0 | 42位时间戳(毫秒) | 3位机器ID | 8位序列号 最大机器ID=7,同1毫秒时可用最大序列号:255,可用:139年
// ---可用方案4:0 | 43位时间戳(毫秒) | 3位机器ID | 7位序列号 最大机器ID=7,同1毫秒时可用最大序列号:127,可用:278年
// 生成效率: (8核) 500-1000万ID/秒的生成速率
// 本方法支持年份:采用方案2支持 69年 + 基础年 = 69+2024=2093年 ,之后时间戳会溢出,导致ID重复。
const (
snowflake53BaseEpoch = 1704067200000 // 基础年 2024-01-01 08:00:00 UTC (毫秒)
snowflake53TimeBits = 41 // 时间戳位数
snowflake53MachineBits = 3 // 机器ID位数
snowflake53SequenceBits = 9 // 序列号位数
snowflake53MaxMachineID = -1 ^ (-1 << snowflake53MachineBits) // 最大机器ID值
snowflake53MaxSequence = -1 ^ (-1 << snowflake53SequenceBits) // 最大序列号值
snowflake53MaxClockBackwardTolerance = 5 // 允许最多 5 毫秒的时钟回拨
)
type snowflake53 struct {
machineID int
state atomic.Int64 // 状态:第[0,snowflake53SequenceBits] 位=序列号,挨着snowflake53TimeBits位=时间戳
}
// 创建53位雪花ID生成器
func NewSnowflake53(machineID int) (*snowflake53, error) {
if machineID < 0 || machineID > snowflake53MaxMachineID {
return nil, fmt.Errorf("machineID must be between 0 and %d", snowflake53MaxMachineID)
}
return &snowflake53{
machineID: machineID,
state: atomic.Int64{}, // 初始状态为0
}, nil
}
// 生成下一个ID(支持小范围时钟回拨容忍)
func (s *snowflake53) NextID() (int64, error) {
for {
now := time.Now().UnixMilli()
rawTimestamp := now - snowflake53BaseEpoch
if rawTimestamp < 0 {
return 0, fmt.Errorf("system clock before %s", time.UnixMilli(snowflake53BaseEpoch).Format("2006-01-02 15:04:05"))
}
state := s.state.Load()
lastTimestamp := state >> snowflake53SequenceBits
seq := state & int64(snowflake53MaxSequence)
var useTimestamp int64
var shouldIncrementSeq bool
if rawTimestamp >= lastTimestamp {
useTimestamp = rawTimestamp
shouldIncrementSeq = rawTimestamp == lastTimestamp
} else {
// 时钟回拨
backward := lastTimestamp - rawTimestamp
if backward <= snowflake53MaxClockBackwardTolerance {
// 允许的回拨毫秒数
// 容忍范围内:继续生成
useTimestamp = lastTimestamp
shouldIncrementSeq = true
} else {
// 超出容忍范围:触发重试机制
// 尝试最多 10 次,每次等 1ms,看时钟是否追上
for attempt := 0; attempt < 10; attempt++ {
time.Sleep(time.Millisecond)
now = time.Now().UnixMilli()
rawTimestamp = now - snowflake53BaseEpoch
if (lastTimestamp - rawTimestamp) <= snowflake53MaxClockBackwardTolerance {
useTimestamp = rawTimestamp
shouldIncrementSeq = rawTimestamp <= lastTimestamp
goto systemClockRecoverd
}
}
// 重试失败,报错
return 0, fmt.Errorf(
"system clock moved backwards by %d ms (tolerance=%d ms, retries=%d), last=%d, current=%d",
lastTimestamp-(now-snowflake53BaseEpoch),
snowflake53MaxClockBackwardTolerance,
10,
lastTimestamp+snowflake53BaseEpoch,
now,
)
}
}
systemClockRecoverd:
var newSeq int64
if shouldIncrementSeq {
newSeq = (seq + 1) & int64(snowflake53MaxSequence)
if newSeq == 0 {
// 序列号溢出:等待进入下一毫秒
for time.Now().UnixMilli()-snowflake53BaseEpoch <= useTimestamp {
time.Sleep(time.Millisecond)
}
continue
}
} else {
newSeq = 0
}
// 安全截断时间戳(防止位溢出)
useTimestamp &= (1 << snowflake53TimeBits) - 1
// CAS 更新状态
newState := (useTimestamp << snowflake53SequenceBits) | newSeq
if s.state.CompareAndSwap(state, newState) {
// 组合ID: 时间戳 | 机器ID | 序列号
id := useTimestamp << (snowflake53MachineBits + snowflake53SequenceBits)
id |= int64(s.machineID) << snowflake53SequenceBits
id |= newSeq
return id, nil
}
// CAS 失败,重试整个流程
}
}
// 解析ID(返回时间戳、机器ID、序列号)
func (s *snowflake53) ParseID(id int64) (timestamp, machineID, sequence int64) {
sequence = id & int64(snowflake53MaxSequence)
machineID = (id >> snowflake53SequenceBits) & int64(snowflake53MaxMachineID)
timestamp = (id >> (snowflake53MachineBits + snowflake53SequenceBits)) + snowflake53BaseEpoch
return
}