自定义ID生成,雪花算法(53位)避免超过9007199254740991最大值,超过前端JS精度

74 阅读5分钟

自定义雪花算法(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
}