一:数据库主键生成:利用数据库的主键生成来获取唯一ID,但是这种方式依赖数据库组件,并且获取ID的效率也不高。
二:128位的UUID生成:这种方法可以生成128位的UUID,据官方说UUID每秒可以生成100万个,并且等到100年重复的概率才达到50%。但是这个方案有个缺点是UUID不是有序增长的。
三:工单服务器:其实也就是一个服务利用数据库自增主键来获取ID,并且响应请求。但这个服务很可能会有单点故障的问题。
四:雪花ID生成器:这是比较好用的方案,具有ID有序,长度是64位数字,不重复的特点,且可以自定义ID的位数来适配不同的业务的要求。
package SnowFlakeYYtest
import (
"errors"
"sync"
"time"
)
// 手写雪花ID生成器, 分布式唯一ID生成器,ID具有64位,随时间戳增长有序,不重复。
// 雪花ID 64位 第一位是符号位,一般是0
// 1-42位是毫秒时间戳位(41位)
// 5位工作中心, 最多可以表示32个数据中心
// 5位机器id,最多可以有32台机器
// 12位序号位,同一毫秒能生成4096个不同的ID
const (
// 数据中心位数
centerBits uint8 = 5
// 机器id位数
workerBits uint8 = 5
// 开始毫秒时间戳 北京时间:2024-03-26 16:34:24
epoch int64 = 1711442064000
// 序号位数
seqBits uint8 = 12
// 检测数据中心是否溢出
centerMax int64 = -1 ^ (-1 << centerBits)
// 检测机器中心是否溢出
workerMax int64 = -1 ^ (-1 << workerBits)
// 检测序号是否溢出
seqMax int64 = -1 ^ (-1 << seqBits)
// 时间戳左移位数
timeShift uint8 = centerBits + workerBits + seqBits
// 数据中心左移位数
centerShift uint8 = workerBits + seqBits
// 机器id左移位数
workerShift uint8 = seqBits
)
type Worker struct {
mu sync.Mutex
// 开始毫秒时间戳 北京时间:2024-03-26 16:34:24
epoch int64
centerId int64
workerId int64
// 当前毫秒已经生成的序列号,从0开始累加
seq int64
// 上次生成的毫秒时间戳,用来检查时钟回退
lastTimestamp int64
}
// 返回一个Worker实例
func New(centerId int64, workerId int64) (*Worker, error) {
if centerId < 0 || centerId > centerMax || workerId < 0 || workerId > workerMax {
return nil, errors.New("Incorrect CenterId Or WorkerId")
}
return &Worker{
workerId: workerId,
centerId: centerId,
}, nil
}
/**
获取序列号
*/
func (this *Worker) NextId() (int64, error) {
this.mu.Lock()
defer this.mu.Unlock()
currentTimestamp := time.Now().UnixMilli()
if currentTimestamp < this.lastTimestamp {
return -1, errors.New("Time Sync Error")
}
if currentTimestamp == this.lastTimestamp {
this.seq = (this.seq + 1) & seqMax
if this.seq == 0 {
// 环状,超过了当前毫秒能够获取的最大序列号,那么就自旋等待下一个毫秒
for currentTimestamp <= this.lastTimestamp {
currentTimestamp = time.Now().UnixMilli()
}
}
} else {
// 当前时间和上一个毫秒数不一致, 返回0
this.seq = 0
}
this.lastTimestamp = currentTimestamp
return ((currentTimestamp - epoch) << timeShift) |
(this.centerId << centerShift) |
(this.workerId << workerShift) |
this.seq, nil
}
package SnowFlakeYYtest
import (
"context"
"testing"
)
func TestGetId(t *testing.T) {
const cnt int = 10000
worker, err := New(1,1)
if err != nil {
t.Fatalf("Init Worker Error")
return
}
// 启动1万个协程同时访问
hm := make(map[int64]struct{})
ch := make(chan int64, cnt)
defer close(ch)
done := make(chan struct{})
defer close(done)
t.Logf("开始生成\n")
go func() {
i := 0
for {
select {
case num := <- ch:
i++
if _,ok := hm[num]; ok {
t.Errorf("SnowFlake Id is not unique !\n")
done <- struct{}{}
return
}
hm[num] = struct{}{}
if i == cnt {
t.Logf("取完%d次数据了,监控协程退出\n", cnt)
done <- struct{}{}
return
}
}
}
}()
ctx,cancel := context.WithCancel(context.Background())
for i := 0; i < cnt; i++ {
go func(ctx context.Context) {
select {
case <-ctx.Done():
t.Logf("有重复元素, 退出生成\n")
return
default:
id, err := worker.NextId()
if err != nil {
t.Errorf("生成ID错误, %v\n", err)
return
}
ch <- id
}
}(ctx)
}
<-done
cancel()
t.Logf("完成%d次请求, 没有重复ID\n", cnt)
}
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。