雪花算法
学习博客:学会雪花算法就看这一篇就够了(go版本)
介绍
特点:
- 能满足高并发分布式系统环境下ID不重复
- 生成效率高
- 基于时间戳,可以保证基本有序递增
- 不依赖于第三方的库或者中间件
- 生成的
id
具有时序性和唯一性
原理
SnowFlake 算法(雪花算法), 是Twitter开源的分布式id生成算法。其核心思想就是: 使用一个64 bit的long型的数字作为全局唯一id。它的结构如下:
下面我们来对每一部分进一步的分析:
-
符号标识位(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()