雪花算法(SnowFlake)

雪花算法(SnowFlake)是由 Twitter 开源的一种分布式唯一 ID 生成算法,它能够高效地生成全局唯一的 64 位整数 ID,适用于分布式系统环境。

一、算法原理

雪花算法生成的 64 位 ID 的结构如下:

1位符号位41位时间戳10位机器ID12位序列号
  • 1位符号位:最高位未使用,符号位为0,表示这是一个正数。

  • 41位时间戳:记录的是自 2016-01-01 00:00:00 UTC 开始的毫秒数。41位可以表示 2^41 毫秒,约 69 年。

  • 10位机器ID:用于区分不同的机器,最多支持 2^10 = 1024 个节点。

  • 12位序列号:同一毫秒内同一机器生成的 ID 的序列号,最多支持 2^12 = 4096 个。

二、实现步骤

  1. 时间戳计算 每次生成 ID 时,首先获取当前时间戳(精确到毫秒),并减去初始时间戳(通常是 2016-01-01 00:00:00 UTC)以获得一个相对时间戳。

  2. 机器 ID 分配 每台机器在分布式系统中被分配一个唯一的机器 ID,确保在集群中的每个节点都有不同的 ID。

  3. 序列号生成 在同一毫秒内,每个机器节点可以生成多个 ID,序列号用于区分这些 ID。序列号从 0 开始,每生成一个 ID,序列号加 1,直到达到最大值 4095,然后在下一毫秒重置为 0。

  4. 组合 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)
	}
}

四、优点

  1. 全局唯一性 在分布式系统中,不同机器生成的 ID 不会冲突。

  2. 有序性 生成的 ID 是按时间有序的,这有利于数据库的索引性能。

  3. 高效性 生成 ID 的过程非常高效,对系统性能影响很小。

  4. 可排序性 由于 ID 包含时间戳信息,可以根据 ID 的大小进行排序,这在某些场景下非常有用。

  5. 低存储占用 64 位整数占用空间小,适合大规模存储。

五、应用场景

  1. 分布式系统中的唯一 ID 生成 在分布式系统中,多个服务节点需要生成唯一的 ID,雪花算法可以确保全局唯一性。

  2. 数据库主键生成 在数据库中,雪花算法生成的 ID 可以作为主键,避免主键冲突。

  3. 日志追踪 在日志系统中,可以使用雪花算法生成的 ID 作为日志的唯一标识,便于追踪和分析。

  4. 消息队列中的消息 ID 在消息队列中,使用雪花算法生成的消息 ID 可以确保消息的唯一性和有序性。

六、缺点

  1. 依赖机器时间 如果机器时间被调整到过去,可能会导致生成的 ID 出现重复。

  2. 单点故障风险 如果雪花算法的生成服务出现故障,整个系统可能无法生成新的 ID。

  3. 机器 ID 分配复杂 在大规模分布式系统中,分配唯一的机器 ID 可能会比较复杂。

雪花算法是一个高效且实用的分布式唯一 ID 生成算法,适用于多种需要生成唯一 ID 的场景。在实际应用中,可以根据具体需求选择是否使用雪花算法。

自学go语言笔记,希望我们可以一起学习!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值