在 Go 语言中,可以通过实现雪花算法(Snowflake)来生成分布式唯一ID。雪花算法是 Twitter 提出的一种生成分布式唯一ID的算法,其核心思想是将一个64位的ID划分为多个部分,分别表示时间戳、机器ID和序列号。
以下是雪花算法的 Go 语言实现:
1. 雪花算法结构
雪花算法的64位ID结构如下:
| 1 bit (未使用) | 41 bits (时间戳) | 10 bits (机器ID) | 12 bits (序列号) |
- 时间戳:41位,表示从某个起始时间到当前时间的毫秒数。
- 机器ID:10位,用于标识不同的机器或节点。
- 序列号:12位,用于同一毫秒内生成多个ID。
2. Go 实现代码
以下是雪花算法的 Go 实现:
package main
import (
"errors"
"fmt"
"sync"
"time"
)
const (
// 定义各部分的位数
epoch int64 = 1609459200000 // 起始时间:2021-01-01 00:00:00 UTC
timestampBits uint8 = 41
machineIDBits uint8 = 10
sequenceBits uint8 = 12
// 定义最大值
maxMachineID int64 = -1 ^ (-1 << machineIDBits)
maxSequence int64 = -1 ^ (-1 << sequenceBits)
// 定义位移
timestampShift = machineIDBits + sequenceBits
machineIDShift = sequenceBits
)
// Snowflake 结构体
type Snowflake struct {
mutex sync.Mutex
lastStamp int64
machineID int64
sequence int64
}
// NewSnowflake 创建一个 Snowflake 实例
func NewSnowflake(machineID int64) (*Snowflake, error) {
if machineID < 0 || machineID > maxMachineID {
return nil, errors.New("machine ID out of range")
}
return &Snowflake{
lastStamp: 0,
machineID: machineID,
sequence: 0,
}, nil
}
// NextID 生成下一个唯一ID
func (s *Snowflake) NextID() int64 {
s.mutex.Lock()
defer s.mutex.Unlock()
// 获取当前时间戳
currentStamp := time.Now().UnixMilli()
// 如果当前时间戳小于上次生成ID的时间戳,说明时钟回拨
if currentStamp < s.lastStamp {
panic("clock moved backwards")
}
// 如果是同一毫秒内生成的ID,则递增序列号
if currentStamp == s.lastStamp {
s.sequence = (s.sequence + 1) & maxSequence
// 如果序列号溢出,则等待下一毫秒
if s.sequence == 0 {
for currentStamp <= s.lastStamp {
currentStamp = time.Now().UnixMilli()
}
}
} else {
// 如果不是同一毫秒,则重置序列号
s.sequence = 0
}
// 更新上次生成ID的时间戳
s.lastStamp = currentStamp
// 生成ID
id := ((currentStamp - epoch) << timestampShift) |
(s.machineID << machineIDShift) |
s.sequence
return id
}
func main() {
// 创建一个 Snowflake 实例,机器ID为 1
snowflake, err := NewSnowflake(1)
if err != nil {
panic(err)
}
// 生成10个唯一ID
for i := 0; i < 10; i++ {
id := snowflake.NextID()
fmt.Println(id)
}
}
3. 代码说明
- 起始时间:
epoch是算法的起始时间,可以根据需要调整。 - 机器ID:
machineID用于标识不同的机器或节点,必须在[0, maxMachineID]范围内。 - 序列号:
sequence用于同一毫秒内生成多个ID,范围为[0, maxSequence]。 - 线程安全:使用
sync.Mutex确保并发安全。 - 时钟回拨处理:如果当前时间戳小于上次生成ID的时间戳,说明时钟回拨,直接抛出异常。
4. 示例输出
运行上述代码,会生成类似以下的唯一ID:
6928334669447168
6928334669447169
6928334669447170
6928334669447171
6928334669447172
6928334669447173
6928334669447174
6928334669447175
6928334669447176
6928334669447177
5. 优点
- 高性能:生成ID的速度非常快。
- 分布式:通过机器ID支持分布式部署。
- 有序性:ID 按时间递增,适合作为数据库主键。
6. 注意事项
- 机器ID分配:确保每台机器的
machineID唯一。 - 时钟同步:确保各机器的时钟同步,避免时钟回拨问题。
- ID长度:生成的ID是64位整数,适合大多数场景。