golang,go,博客,开源,编程

golang实现雪花算法

Published on with 0 views and 0 comments

雪花算法(Snowflake Algorithm)在 Go 语言中的实现,基本的思路和原理与其他语言相同,依然是将 64 位整型(int64)划分为几个部分,分别表示时间戳、机器 ID、数据中心 ID 和序列号等信息。以下是一个完整的 Go 语言实现雪花算法的例子。

雪花算法 Go 实现

package main

import (
	"fmt"
	"sync"
	"time"
)

const (
	// 初始时间戳:自定义的纪元时间(通常设置为某个固定的起始时间)
	epoch        int64 = 1609459200000 // 2021年1月1日 00:00:00的毫秒级时间戳
	workerBits   uint8 = 10            // 机器 ID 的位数
	sequenceBits uint8 = 12            // 序列号的位数

	workerMax      int64 = -1 ^ (-1 << workerBits)  // 最大机器 ID (1023)
	sequenceMask   int64 = -1 ^ (-1 << sequenceBits) // 最大序列号 (4095)
	workerShift    uint8 = sequenceBits             // 机器 ID 的位移量
	timestampShift uint8 = workerBits + workerShift // 时间戳的位移量
)

// Snowflake 结构体,包含了当前机器的 ID 和生成 ID 所需要的状态
type Snowflake struct {
	mu            sync.Mutex
	lastTimestamp int64 // 上次生成 ID 时的时间戳
	workerID      int64 // 当前机器的 ID
	sequence      int64 // 当前毫秒内的序列号
}

// NewSnowflake 创建一个新的 Snowflake 实例
func NewSnowflake(workerID int64) (*Snowflake, error) {
	if workerID < 0 || workerID > workerMax {
		return nil, fmt.Errorf("worker ID should be between 0 and %d", workerMax)
	}
	return &Snowflake{
		workerID: workerID,
	}, nil
}

// generateID 生成一个唯一的雪花 ID
func (s *Snowflake) generateID() int64 {
	s.mu.Lock() // 保证线程安全
	defer s.mu.Unlock()

	// 获取当前时间戳(单位:毫秒)
	timestamp := time.Now().UnixMilli() - epoch

	// 如果当前时间戳和上次生成 ID 的时间戳相同,递增序列号
	if timestamp == s.lastTimestamp {
		s.sequence = (s.sequence + 1) & sequenceMask
		// 如果序列号已经达到最大值,等待下一毫秒
		if s.sequence == 0 {
			for timestamp <= s.lastTimestamp {
				timestamp = time.Now().UnixMilli() - epoch
			}
		}
	} else {
		// 不同时间戳,重置序列号
		s.sequence = 0
	}

	s.lastTimestamp = timestamp

	// 根据雪花算法的位分配生成最终的 ID
	id := (timestamp << timestampShift) |
		(s.workerID << workerShift) |
		s.sequence

	return id
}

func main() {
	// 创建一个 Snowflake 实例,指定机器 ID(这里为1)
	sf, err := NewSnowflake(1)
	if err != nil {
		fmt.Println("Error:", err)
		return
	}

	// 生成并打印多个 ID
	for i := 0; i < 10; i++ {
		id := sf.generateID()
		fmt.Printf("Generated ID: %d\n", id)
	}
}

代码解析

  1. 常量定义
    • epoch: 自定义的纪元时间(起始时间),通常是某个固定的时间戳。这里选择了 2021 年 1 月 1 日 00:00:00 的毫秒级时间戳。
    • workerBits: 机器 ID 所占的位数。通过调整该值可以改变集群中可支持的机器数量(例如,workerBits 为 10 时,最多支持 1024 台机器)。
    • sequenceBits: 序列号的位数,决定了每个机器在同一毫秒内可以生成多少个唯一的 ID(例如,sequenceBits 为 12 时,最多支持每毫秒生成 4096 个 ID)。
  2. 结构体 Snowflake
    • workerID: 当前机器的 ID,用于区分不同机器生成的 ID。
    • sequence: 当前毫秒内生成的 ID 序列号,从 0 开始递增,最多为 4095。
    • lastTimestamp: 记录上一次生成 ID 时的时间戳,确保在同一毫秒内生成不同的 ID。
  3. generateID 方法
    • 获取当前的时间戳(当前时间减去纪元时间)。
    • 如果当前时间戳与上一次生成 ID 的时间戳相同,则递增序列号。序列号会在每毫秒内递增,最多支持 4096 个 ID。
    • 如果序列号达到最大值(即 4095),则等待下一毫秒生成 ID。
    • 如果当前时间戳与上次的时间戳不同,则重置序列号。
    • 生成 ID 的最终值由以下几个部分拼接而成:
      • timestamp << timestampShift: 时间戳左移后的值。
      • s.workerID << workerShift: 机器 ID 左移后的值。
      • s.sequence: 序列号。
  4. NewSnowflake 方法
    • 用于创建一个新的 Snowflake 实例,传入机器 ID,并校验该 ID 是否有效。
  5. main 方法
    • 创建一个 Snowflake 实例并生成多个 ID,打印输出。

运行示例

假设我们运行上面的代码,生成并打印 10 个 ID,输出可能如下所示:

Generated ID: 1654767740152535040
Generated ID: 1654767740152540160
Generated ID: 1654767740152545280
Generated ID: 1654767740152550400
Generated ID: 1654767740152555520
Generated ID: 1654767740152560640
Generated ID: 1654767740152565760
Generated ID: 1654767740152570880
Generated ID: 1654767740152576000
Generated ID: 1654767740152581120

这些 ID 会按时间顺序递增,可以根据生成的 ID 推算出其生成的时间(通过时间戳部分)。同时,每个 ID 都是全局唯一的,适用于分布式系统中。

雪花算法的特点

  • 高效性:雪花算法的生成过程基于位运算,效率非常高,支持高并发生成 ID。
  • 全局唯一性:通过机器 ID 和序列号等信息,确保每个生成的 ID 是唯一的。
  • 时间递增性:生成的 ID 中包含时间戳信息,保证了 ID 的顺序性。

适用场景

  • 分布式系统中的唯一 ID 生成,避免数据库的自增 ID 或全局唯一的 UUID 生成瓶颈。
  • 微服务架构中的 ID 生成,避免中心化 ID 服务成为瓶颈。
  • 高并发的场景下,生成唯一且递增的 ID(例如订单系统、日志系统等)。

需要注意的问题

  • 时间回拨:雪花算法依赖系统时钟,如果系统时间发生回拨,可能会导致生成的 ID 不按时间递增。为了避免这种问题,可以在实现时加上时间回拨的处理逻辑。
  • 机器 ID 配置:需要根据部署的机器数量合理设置机器 ID 位数,避免出现 ID 冲突。

通过上述实现和原理分析,雪花算法为分布式系统提供了一种高效、简单且可靠的全局唯一 ID 生成方案。


标题:golang实现雪花算法
作者:mooncakeee
地址:http://blog.dd95828.com/articles/2025/01/06/1736152727024.html
联系:scotttu@163.com