uuid
是什么
uuid的全称是 universally unique identifier ,即通用唯一识别号,它由16字节(128byte)组成,可以用于唯一的标识信息。
格式为 8-4-4-4-12的16进制格式
123e4567-e89b-12d3-a456-426614174000
使用 - 分隔
不同的UUID版本
1. UUID v1 (基于时间)
UUID v1 是基于时间戳生成的,并使用了机器的MAC地址来保证唯一性。其结构如下:
- 前 60 位表示时间戳,单位是100纳秒(从1582年10月15日以来的时间)。
- 13 位表示时钟序列,用来防止同一时间生成多个UUID时产生冲突。
- 剩下的 48 位为节点标识符,通常为设备的 MAC 地址。
由于包含MAC地址,因此UUID v1可能会暴露硬件信息。
2. UUID v2 (DCE 安全)
UUID v2 类似于 v1,但根据用户的本地标识(如UID或GID)和时间戳生成。这种UUID较少使用。
3. UUID v3 (基于命名空间的散列)
UUID v3 使用命名空间和用户指定的名称生成。它基于MD5哈希算法,即根据命名空间和名称的MD5哈希值生成唯一标识符。此版本可以确保同一名称在同一命名空间下生成的UUID始终相同。
命名空间有四种标准类型:
DNS:基于域名系统URL:基于URLOID:基于对象标识符X.500 DN:基于X.500目录名
4. UUID v4 (随机生成)
UUID v4 是目前最常用的版本,使用随机数生成器生成UUID。其128位中的122位是随机生成的,剩余6位用于版本信息和其他元数据。这使得v4版本的UUID具有较高的随机性和唯一性。
生成UUID v4的关键是使用高质量的随机数生成器,保证足够的随机性。
5. UUID v5 (基于命名空间的散列)
UUID v5 类似于 v3,但它使用的是SHA-1哈希算法而不是MD5,因此具有更高的安全性。和v3一样,v5生成的UUID对于相同的命名空间和名称也会一致。
使用场景
- 文件、资源的唯一标识
- 会话标识符
- 分布式系统中的标识符:微服务架构中生成全局唯一的请求 ID、事务 ID、任务 ID 等。
- 消息队列中的信息ID
雪花算法
是什么
0 - 0000000000 0000000000 0000000000 0000000000 0 - 0000000000 - 000000000000
雪花算法生成的ID通常是一个64位的整数,它的位结构如下:
- 第一段1位未使用,永远固定为0,因为ID为正数
- 第二段41位,为毫秒级时间(41位的长度可以使用69年)
- 第三段10位,为workID(10位的长度最多能部署1024个节点)
- 第四段为12位,表示每毫秒内的计数(12位的计数顺序号支持每个节点每毫秒支持产生4096个ID序号)
按照 1024个节点计算:每毫秒可以生成的ID序列号有 1024*4096=4194304,足以满足绝大多数业务场景
- 获取当前时间戳:计算当前时间与纪元时间的差值,以毫秒为单位。
- 生成ID:
- 将时间戳左移(41位),数据中心ID左移(5位),机器ID左移(5位),并将序列号添加到最低位。
- 组合所有部分,生成一个64位的整数ID。
((当前时间 - 服务时间) << timestampLeftShift)
| (机器ID << workerIdShift)
| sequence;
雪花算法存在的问题
SnowFlake强依赖 时间戳,所以时间的变动会造成SnowFlake的算法产生错误;使用时需要注意时钟回拨的现象
具体实现
package main
import (
"errors"
"fmt"
"sync"
"time"
)
const (
workerIDBits = 10 // 节点ID长度
sequenceBits = 12 // 序列号长度
workerIDShift = sequenceBits // 节点ID左移位数
timestampShift = sequenceBits + workerIDBits // 时间戳左移位数
sequenceMask = -1 ^ (-1 << sequenceBits) // 序列号掩码
epoch = int64(1577836800000) // 起始时间戳(2020-01-01)
maxWorkerID = -1 ^ (-1 << workerIDBits) // 最大节点ID
)
type Snowflake struct {
mu sync.Mutex
timestamp int64
workerID int64
sequence int64
}
func NewSnowflake(workerID int64) (*Snowflake, error) {
if workerID < 0 || workerID > maxWorkerID {
return nil, errors.New("worker ID out of range")
}
return &Snowflake{
timestamp: 0,
workerID: workerID,
sequence: 0,
}, nil
}
func (s *Snowflake) tilNextMillis(lastTimestamp int64) int64 {
timestamp := time.Now().UnixNano() / int64(time.Millisecond)
for timestamp <= lastTimestamp {
timestamp = time.Now().UnixNano() / int64(time.Millisecond)
}
return timestamp
}
func (s *Snowflake) NextID() (int64, error) {
s.mu.Lock()
defer s.mu.Unlock()
timestamp := time.Now().UnixNano() / int64(time.Millisecond)
if timestamp < s.timestamp {
return 0, fmt.Errorf("clock moved backwards. Refusing to generate id for %d milliseconds", s.timestamp-timestamp)
}
if timestamp == s.timestamp {
s.sequence = (s.sequence + 1) & sequenceMask
if s.sequence == 0 {
timestamp = s.tilNextMillis(s.timestamp)
}
} else {
s.sequence = 0
}
s.timestamp = timestamp
id := ((timestamp - epoch) << timestampShift) |
(s.workerID << workerIDShift) |
s.sequence
return id, nil
}
func main() {
sf, err := NewSnowflake(1)
if err != nil {
fmt.Printf("Error initializing Snowflake: %v\n", err)
return
}
for i := 0; i < 10; i++ {
id, err := sf.NextID()
if err != nil {
fmt.Printf("Error generating ID: %v\n", err)
continue
}
fmt.Println(id)
}
}