雪花算法(SnowFlake)是由 Twitter 开源的一种分布式唯一 ID 生成算法,它能够高效地生成全局唯一的 64 位整数 ID,适用于分布式系统环境。
一、算法原理
雪花算法生成的 64 位 ID 的结构如下:
1位符号位 | 41位时间戳 | 10位机器ID | 12位序列号 |
---|
-
1位符号位:最高位未使用,符号位为0,表示这是一个正数。
-
41位时间戳:记录的是自 2016-01-01 00:00:00 UTC 开始的毫秒数。41位可以表示 2^41 毫秒,约 69 年。
-
10位机器ID:用于区分不同的机器,最多支持 2^10 = 1024 个节点。
-
12位序列号:同一毫秒内同一机器生成的 ID 的序列号,最多支持 2^12 = 4096 个。
二、实现步骤
-
时间戳计算 每次生成 ID 时,首先获取当前时间戳(精确到毫秒),并减去初始时间戳(通常是 2016-01-01 00:00:00 UTC)以获得一个相对时间戳。
-
机器 ID 分配 每台机器在分布式系统中被分配一个唯一的机器 ID,确保在集群中的每个节点都有不同的 ID。
-
序列号生成 在同一毫秒内,每个机器节点可以生成多个 ID,序列号用于区分这些 ID。序列号从 0 开始,每生成一个 ID,序列号加 1,直到达到最大值 4095,然后在下一毫秒重置为 0。
-
组合 ID 将时间戳、机器 ID 和序列号按位组合成一个 64 位的整数。
三、Go 语言实现代码
package main
import (
"fmt"
"sync"
"time"
)
const (
startTimestamp = 1451606400000 // 2016-01-01 00:00:00 UTC
workerIdBits = 10 // 机器 ID 所占位数
sequenceBits = 12 // 序列号所占位数
maxWorkerId = -1 ^ (-1 << workerIdBits)
sequenceMask = -1 ^ (-1 << sequenceBits)
workerIdShift = sequenceBits
timestampLeftShift = sequenceBits + workerIdBits
)
var (
sequence uint64
lastTimestamp uint64
workerId uint64
lock sync.Mutex
)
func InitSnowflake(workerId uint64) {
if workerId > maxWorkerId || workerId < 0 {
panic(fmt.Sprintf("worker Id can't be greater than %d or less than 0", maxWorkerId))
}
lock.Lock()
defer lock.Unlock()
workerId = workerId
sequence = 0
lastTimestamp = 0
}
func GenerateSnowflakeId() uint64 {
lock.Lock()
defer lock.Unlock()
currentTimestamp := getTimestamp()
if currentTimestamp < lastTimestamp {
panic(fmt.Sprintf("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp-currentTimestamp))
}
if currentTimestamp == lastTimestamp {
sequence = (sequence + 1) & sequenceMask
if sequence == 0 {
currentTimestamp = tilNextMillis(lastTimestamp)
}
} else {
sequence = 0
}
lastTimestamp = currentTimestamp
return (currentTimestamp - startTimestamp) << timestampLeftShift |
(workerId << workerIdShift) |
sequence
}
func tilNextMillis(lastTimestamp uint64) uint64 {
timestamp := getTimestamp()
for timestamp <= lastTimestamp {
timestamp = getTimestamp()
}
return timestamp
}
func getTimestamp() uint64 {
return uint64(time.Now().UnixNano() / 1e6)
}
func main() {
InitSnowflake(1)
for i := 0; i < 10; i++ {
id := GenerateSnowflakeId()
fmt.Println(id)
}
}
四、优点
-
全局唯一性 在分布式系统中,不同机器生成的 ID 不会冲突。
-
有序性 生成的 ID 是按时间有序的,这有利于数据库的索引性能。
-
高效性 生成 ID 的过程非常高效,对系统性能影响很小。
-
可排序性 由于 ID 包含时间戳信息,可以根据 ID 的大小进行排序,这在某些场景下非常有用。
-
低存储占用 64 位整数占用空间小,适合大规模存储。
五、应用场景
-
分布式系统中的唯一 ID 生成 在分布式系统中,多个服务节点需要生成唯一的 ID,雪花算法可以确保全局唯一性。
-
数据库主键生成 在数据库中,雪花算法生成的 ID 可以作为主键,避免主键冲突。
-
日志追踪 在日志系统中,可以使用雪花算法生成的 ID 作为日志的唯一标识,便于追踪和分析。
-
消息队列中的消息 ID 在消息队列中,使用雪花算法生成的消息 ID 可以确保消息的唯一性和有序性。
六、缺点
-
依赖机器时间 如果机器时间被调整到过去,可能会导致生成的 ID 出现重复。
-
单点故障风险 如果雪花算法的生成服务出现故障,整个系统可能无法生成新的 ID。
-
机器 ID 分配复杂 在大规模分布式系统中,分配唯一的机器 ID 可能会比较复杂。
雪花算法是一个高效且实用的分布式唯一 ID 生成算法,适用于多种需要生成唯一 ID 的场景。在实际应用中,可以根据具体需求选择是否使用雪花算法。
自学go语言笔记,希望我们可以一起学习!