雪花算法实现

雪花算法实现

雪花算法

0 - 0000000000 0000000000 0000000000 0000000000 0 - 0000000000 - 000000000000

符号位 时间戳 机器码 序列号

41位毫秒级时间戳,10位机器id 12位序列号 这个起始时间sEpoch其实不太重要,因为一般业务不会超过10年,这个69-(10~20)总还有50年的余量的,比较重要的一点就是如果一个毫秒内生产的序列id 过多就得等待到下一个毫秒窗口内

代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package utils

// https://sourcegraph.com/github.com/sohaha/zlsgo/-/blob/zstring/snowflake.go
// 学习 zlsgo的总结
import (
	"fmt"
	"sync"
	"time"
)

const (
	sEpoch     = 1474802888000 // 这个可以是业务上线的时间
	TimeBase   = int64(1000000)
	MaxWorkID  = 1<<5 - 1
	MaxDataID  = MaxWorkID
	MaxSeqID   = 1<<12 - 1
	MaskWorkID = 1<<10 - 1
	MaskSeqID  = 1<<12 - 1
)

type SnowFlakeBuilder struct {
	start  int64
	workid int64
	seqid  int64
	lock   sync.Mutex
}

func NewBuilder(st time.Time, data int, worker int) *SnowFlakeBuilder {
	if data < 0 || data > MaxDataID || worker < 0 || worker > MaxWorkID || st.After(time.Now()) {
		panic("invaild argument error")
	}
	return &SnowFlakeBuilder{
		start:  st.UnixNano() / TimeBase,
		workid: int64(data<<5|worker) & MaskWorkID,
	}
}

// 类似自旋锁的逻辑,持续试图获取下一毫秒的时间资源
func (s *SnowFlakeBuilder) waitToNextMS(last int64) int64 {
	ts := time.Now().UnixNano() / TimeBase
	for {
		if ts <= last {
			ts = time.Now().UnixNano() / TimeBase
		} else {
			break
		}
	}
	return ts
}

func (s *SnowFlakeBuilder) GetID() (int64, error) {
	s.lock.Lock()
	defer s.lock.Unlock()
	ts := time.Now().UnixNano() / TimeBase
	if ts == s.start {
		s.seqid = (s.seqid + 1) & MaskSeqID
		if s.seqid == 0 {
			ts = s.waitToNextMS(ts)
		}
	} else {
		s.seqid = 0
	}
	if ts < s.start {
		return 0, fmt.Errorf("clock moved backwards, refuse gen id")
	}
	s.start = ts
	ts = (ts-sEpoch)<<22 | s.workid<<12 | s.seqid
	return ts, nil
}
Licensed under CC BY-NC-SA 4.0
往日已经不在,未来尚未开始
使用 Hugo 构建
主题 StackJimmy 设计