【阅读 GinSkeleton】雪花算法

雪花算法

学习博客:学会雪花算法就看这一篇就够了(go版本)

分布式id自增算法-雪花算法(snowflake)

介绍

特点:

  • 能满足高并发分布式系统环境下ID不重复
  • 生成效率高
  • 基于时间戳,可以保证基本有序递增
  • 不依赖于第三方的库或者中间件
  • 生成的id具有时序性和唯一性

原理

SnowFlake 算法(雪花算法), 是Twitter开源的分布式id生成算法。其核心思想就是: 使用一个64 bit的long型的数字作为全局唯一id。它的结构如下:

img

下面我们来对每一部分进一步的分析:

  • 符号标识位(1位):计算机中为了区分负数(1)和正数(0),设计者将第一位做为符号位,ID通常使用正数,因此最高位固定为0;(不使用)

  • 41位时间截(毫秒),这个是使用 当前时间 减去 开始时间 得到的值;因此一旦我们的算法投入使用,那么程序中设置的开始时间就不能再去随意更改了,否则将可能出现重复的id值; 由于是基于时间来实现的且只有41位,由此可以计算出该算法只能使用70年左右: (2^41)/(1000*60*60*24*365) = 69.7 年

  • 10位机器ID:共计1024个节点,通常将其分为2部分:机房ID(dataCenterId) 和 机器ID(workerId);

  • 12 位序列号:毫秒内的计数,共计4098个;简单来说就是每毫秒内从0开始计算得到值;

最终SnowFlake算法总结如下:整体上按照时间自增排序,并且整个分布式系统内不会产生ID 碰撞(由机房ID和机器ID作区分),并且效率较高。最多支持1024台机器,每台机器每毫秒能够生成最多4096个ID,整个集群理论上每秒可以生成 1024 * 1000 * 4096 = 42 亿个ID

实现

定义常量

	//SnowFlake 雪花算法 常量
	StartTimeStamp = int64(1483228800000) //开始时间截 (2017-01-01)
	MachineIdBits  = uint(10)             //机器id所占的位数
	SequenceBits   = uint(12)             //序列所占的位数
	SequenceMask   = int64(-1 ^ (-1 << SequenceBits)) //节点ID的最大值 用于防止溢出
	MachineIdShift = SequenceBits                     //机器id左移位数
	TimestampShift = SequenceBits + MachineIdBits     //时间戳左移位数
复制代码

工作节点

type snowflake struct {
	sync.Mutex //添加互斥锁,确保并发安全性
	timestamp int64 //记录上一次生成ID的时间戳
	machineId int64 //机器号
	sequence  int64 //当前毫秒已经生成的ID序列号(从0 开始累加) 1毫秒内最多生成4096个ID
}
复制代码

创建一个生成id的工厂

//utils\snow_flake\snowflake_interf
type InterfaceSnowFlake interface {
	GetId() int64
}
//\utils\snow_flake
// 创建一个雪花算法生成器(生成工厂)
func CreateSnowflakeFactory() snowflake_interf.InterfaceSnowFlake {
	return &snowflake{
		timestamp: 0,
		machineId: variable.ConfigYml.GetInt64("SnowFlake.SnowFlakeMachineId"),
		sequence:  0,
	}
}
复制代码

创建id

// 生成分布式ID
func (s *snowflake) GetId() int64 {
	//加锁
	s.Lock()
	defer func() {
		s.Unlock()
	}()
	//获取当前时间的时间戳(毫秒)
	now := time.Now().UnixNano() / 1e6
	//当前时间与工作节点上一次生成ID的时间
	if s.timestamp == now {
		//相当于sequence++,也检测是否溢出
		s.sequence = (s.sequence + 1) & consts.SequenceMask
		//毫秒内序列溢出
		if s.sequence == 0 {
			//阻塞到下一个毫秒,获得新的时间戳
			for now <= s.timestamp {
				now = time.Now().UnixNano() / 1e6
			}
		}
	} else {
		s.sequence = 0
	}
	//记录上一次生成ID的时间戳
	s.timestamp = now
	//移位并通过或运算拼到一起组成64位的ID
    //时间戳左移22位,机器id左移10位,序列号   或运算 遇0不变
	r := (now-consts.StartTimeStamp)<<consts.TimestampShift | (s.machineId << consts.MachineIdShift) | (s.sequence)
	return r
}

复制代码

初始化

	// 7.雪花算法全局变量
	variable.SnowFlake = snow_flake.CreateSnowflakeFactory()
复制代码

Guess you like

Origin juejin.im/post/7034096474166149156