golang版雪花算法|青训营笔记

325 阅读2分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的第1篇笔记

雪花算法介绍

它是Twitter开源的分布式ID生成算法,其可以在分布式系统中生成全局唯一ID,而且相较于利用数据库主键自增特性,不同节点直接使用自增的ID有很多优势。

雪花算法优点

1、生成时不依赖数据库,完全在内存中生成 2、生成效率高,能达到每秒数百万全局唯一自增ID

雪花算法生成ID组成

生成ID为默认64bit的整数(可以按场景自行配置长度,如增加时间戳的位数,为了支持更多机器节点,可以增加标识位的长度),且按照时间递增,由符号位,时间戳,机器码,序列号组成。 如图: snow.webp 1、首位为符号位,因为ID都是为正整数,第一位默认为0 2、时间戳:占41bit,毫秒级时间戳 3、标识符:占10bit,通常由5bit的数据中心ID和5bit的工作机器ID组成

雪花算法golang实现

const (
	//机器ID
	workerIDBits = uint64(5)
	//数据中心ID
	dataCenterIDBits = uint64(5)
	//序列号
	sequenceBits = uint64(12)
	//最大机器ID  以8位的有符号整数为例,-1的二进制11111111 其右移workerIDBits(5)位 结果为11100000 ,
	//与11111111异或得00011111 即2^5-1 为最大机器ID
	maxWorkerID = int64(-1) ^ (int64(-1) << workerIDBits)

	maxDataCenterID = int64(-1) ^ (int64(-1) << dataCenterIDBits)
	maxSequence     = int64(-1) ^ (int64(-1) << sequenceBits)
	//分别代表偏移量,以时间戳为例,它离最低位差22位,在分别生成序列号、机器id,时间撮之后需要进行右移,在进行
	//或运算即可得到ID
	timeLeft = uint8(22)
	dataLeft = uint8(17)
	workLeft = uint8(12)

	twepoch = int64(1589923200000)
)
type Worker struct {
	mu           sync.Mutex //锁
	LastStamp    int64      //上一个时间戳
	WorkerID     int64      //机器ID
	DataCenterId int64      //数据中心ID
	Sequence     int64      //序列号
}

func NewWorker(WorkerID, dataCenterID int64) *Worker {
	return &Worker{
		WorkerID:     WorkerID,
		LastStamp:    0,
		Sequence:     0,
		DataCenterId: dataCenterID,
	}
}
func (w *Worker) getMilliSeconds() int64 {
	return time.Now().Unix() / 1e6
}
func (w *Worker) NextID() (uint64, error) {
	w.mu.Lock()
	defer w.mu.Unlock()
	return w.nextID()
}
func (w *Worker) nextID() (uint64, error) {
	timeStamp := w.getMilliSeconds()
	if timeStamp < w.LastStamp {
		return 0, errors.New("time is moving backwards,waiting until")
	}
	if w.LastStamp == timeStamp {
		w.Sequence = (w.Sequence + 1) & maxSequence
		if w.Sequence == 0 {
			//保证时间戳递增
			for timeStamp <= w.LastStamp {
				timeStamp = w.getMilliSeconds()
			}
		}

	} else {
		w.Sequence = 0
	}
	w.LastStamp = timeStamp
	id := ((timeStamp - twepoch) << timeLeft) |
		(w.DataCenterId << dataLeft) |
		(w.WorkerID << workLeft) |
		w.Sequence
	return uint64(id), nil

}

总结

雪花算法是一个优秀的全局生成唯一ID的算法,适用于大多数的场景,但也不是万能的,因为工作机器id只有10bit,意味着最多支持1024机器节点,一旦多于其就不能保证全局id唯一了,可以通过扩展工作id位数解决。此外它还依赖于当前机器时钟,出现时钟回拨,可能就会有主键重复的问题。