分布式唯一ID的生成之雪花算法(Go实现)

分布式唯一ID的生成

背景:

在分布式架构下,唯一序列号生成是我们在设计一个尤其是数据库使用分库分表的时候会常见的一个问题

特性:

  1. 全局唯一,这是基本要求,不能出现重复
  2. 数字类型,趋势递增,后面的ID必须比前面的大
  3. 长度短,能够提高查询效率,这也是从MySQL数据库规范出发的,尤其是ID作为主键时
  4. **信息安全,**如果ID连续生成,势必会泄露业务信息,所以需要无规则不规则
  5. 高可用低延时,ID生成快,能够扛住高并发,延时足够低不至于成为业务瓶颈.

雪花算法:

​ snowflake是推特开源的分布式ID生成算法

  1. 结果: long 型的ID号(64位的ID号)

  2. 核心思想(生成的ID号是64位那么就对64位进行划分赋予特别的含义):
    分布式唯一ID的生成之雪花算法(Go实现)_第1张图片

    41bit-时间戳决定了该算法生成ID号的可用年限.

    10bit-工作机器编号决定了该分布式系统的扩容性即机器数量.

    12bit-序列号决定了每毫秒单机系统可以生成的序列号

  3. 拓展:什么是时间戳?

    北京时间1970年01月01日08时00分00秒到此时时刻的总秒数

  4. 优势:

//实现方法:
package main


import (
	"errors"
	"fmt"
	"sync"
	"time"
)

/*
	雪花算法(snowFlake)的具体实现方案:
 */

type SnowFlake struct{
	mu sync.Mutex
	//雪花算法开启时的起始时间戳
	twepoch int64

	//每一部分占用的位数
	workerIdBits     int64 //每个数据中心的工作机器的编号位数
	datacenterIdBits int64 //数据中心的编号位数
	sequenceBits     int64 //每个工作机器每毫秒递增的位数

	//每一部分最大的数值
	maxWorkerId int64
	maxDatacenterId int64
	maxSequence int64

	//每一部分向左移动的位数
	workerIdShift int64
	datacenterIdShift int64
	timestampShift int64

	//当前数据中心ID号
	datacenterId int64
	//当前机器的ID号
	workerId int64
	//序列号
	sequence int64
	//上一次生成ID号前41位的毫秒时间戳
	lastTimestamp int64
}

/*
	获取毫秒的时间戳
 */
func (s *SnowFlake)timeGen()int64{
	return time.Now().UnixMilli()
}
/*
	获取比lastTimestamp大的当前毫秒时间戳
 */
func (s *SnowFlake)tilNextMills()int64{
	timeStampMill:=s.timeGen()
	for timeStampMill<=s.lastTimestamp{
		timeStampMill=s.timeGen()
	}
	return timeStampMill
}
func (s *SnowFlake)NextId()(int64,error){
	s.mu.Lock()
	defer s.mu.Unlock()
	nowTimestamp:=s.timeGen()//获取当前的毫秒级别的时间戳
	if nowTimestamp=mySnow.maxWorkerId||datacenterId>=mySnow.maxDatacenterId{
		return nil,errors.New("workerId or datacenterId must not higher than max value ")
	}
	mySnow.workerIdShift=mySnow.sequenceBits
	mySnow.datacenterIdShift=mySnow.sequenceBits+mySnow.workerIdBits
	mySnow.timestampShift=mySnow.sequenceBits+mySnow.workerIdBits+mySnow.datacenterIdBits

	mySnow.lastTimestamp=-1
	mySnow.workerId=workerId
	mySnow.datacenterId=datacenterId

	return mySnow,nil
}



func main(){
	//模拟实验是生成并发400W个ID,所需要的时间
	mySnow,_:=NewSnowFlake(0,0)//生成雪花算法
	group:=sync.WaitGroup{}
	startTime:=time.Now()
	generateId:=func (s SnowFlake,requestNumber int){
		for i:=0;i

分布式唯一ID的生成之雪花算法(Go实现)_第2张图片

以上分析生成400WID号只需要803.1006ms(所以单机上可以每秒生成的ID数在400W以上)

优点:

  1. 毫秒数在高位,自增序列在低位,整个ID都是趋势递增
  2. 不依赖数据库等第三方系统,以服务的方式部署,稳定性更高,生成的ID性能也是非常高的
  3. 可以根据自身业务特性分配bit位,非常灵活

缺陷:

1. 依赖机器时钟,如果**机器时钟回拨**,会导致重复ID生成.
1. 在单机上是递增的,但是由于设计到分布式环境下,每台机器上的时钟不可能完全同步,有时候会出现不是全局递增的情况.

如何解决单机系统中时钟回拨问题:

​ 可以分为两种情况:

1. 如果**时间回拨时间较短,比如配置5ms以内**,那么可以直接等候一定的时间,让机器时间追上来
1. 如果**时间回拨时间较长**,我们不能接收这么长的阻塞等候,那么就有两个策略,直接拒绝,抛出异常;或者通过RD时钟回滚

布式环境下,每台机器上的时钟不可能完全同步,有时候会出现不是全局递增的情况.

如何解决单机系统中时钟回拨问题:

​ 可以分为两种情况:

1. 如果**时间回拨时间较短,比如配置5ms以内**,那么可以直接等候一定的时间,让机器时间追上来
1. 如果**时间回拨时间较长**,我们不能接收这么长的阻塞等候,那么就有两个策略,直接拒绝,抛出异常;或者通过RD时钟回滚

参考博客高并发情况下,雪花ID一秒400W个,以及分布式ID算法(详析)

你可能感兴趣的:(分布式系统,分布式)