前言
最近工作上遇到一个雪花算法生成Id重复导致数据库中表主键冲突,导致入库失败的问题,所以顺便学习了一下雪花算法,下面是学习的笔记以及讨论如果解决雪花算法在分布式部署中生成重复Id的问题。
基础概念
snowflake中文的意思是雪花,所以常被称为雪花算法
它是twitter用scala语言编写的一个用于简单规则运算就能高效生成唯一ID的算法,下面是源码地址:
网上还有各种其他语言的版本,思路基本上都是参考上述源码
特性
生成的ID不重复
生成性能高
基于时间戳,可以基本保证有序递增
设计原理
准备工作
bit与byte
bit(位):电脑中存储的最小单位,可以存储二进制中的0或1
byte(字节):一个byte由8个bit组成
如图:
byte和bit.png
而在java中,每个数据类型存储所占的字节数不一样,常用的如下:
int:4 个字节。
short:2 个字节。
long:8 个字节。
byte:1 个字节。
float:4 个字节。
double:8 个字节。
char:2 个字节。
而雪花算法生成的数字,我们定义为long,所以就是8个byte,64bit
假设我们定义 long a = 1L;则在计算机中的存储如下:
long类型的存储.png
也就是可表示的范围为:-9223372036854775808(-2的63次方) ~ 9223372036854775807(2的63次方-1),考虑到生成的唯一值用于数据库主键,所以理论值为0~9223372036854775807(2的63次方-1),容量上肯定能满足业务方了
组成原理
雪花算法生成的Id由:1bit 不用 + 41bit时间戳+10bit工作机器id+12bit序列号,如下图:
雪花ID的组成
不用:1bit,因为最高位是符号位,0表示正,1表示负,所以这里固定为0
时间戳:41bit,服务上线的时间毫秒级的时间戳(为当前时间-服务第一次上线时间),这里为(2^41-1)/1000/60/60/24/365 = 49.7年
工作机器id:10bit,表示工作机器id,用于处理分布式部署id不重复问题,可支持2^10 = 1024个节点
序列号:12bit,用于离散同一机器同一毫秒级别生成多条Id时,可允许同一毫秒生成2^12 = 4096个Id,则一秒就可生成4096*1000 = 400w个Id
说明:上面总体是64位,具体位数可自行配置,如想运行更久,需要增加时间戳位数;如想支持更多节点,可增加工作机器id位数;如想支持更高并发,增加序列号位数
Java版本的具体实现
public class SnowflakeIdWorker {
/** 开始时间截 (建议用服务第一次上线的时间,到毫秒级的时间戳) */
private final long twepoch = 687888001020L;
/** 机器id所占的位数 */
private final long workerIdBits = 10L;
/** 支持的最大机器id,结果是1023 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */
private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
/** 序列在id中占的位数 */
private final long sequenceBits = 12L;
/** 机器ID向左移12位 */
private final long workerIdShift = sequenceBits;
/** 时间截向左移22位(10+12) */
private final long timestampLeftShift = sequenceBits + workerIdBits;
/** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)
* <
* */
private final long sequenceMask = -1L ^ (-1L << sequenceBits);
/** 工作机器ID(0~1024) */
private long workerId;
/** 毫秒内序列(0~4095) */
private long sequence = 0L;
/** 上次生成ID的时间截 */
private long lastTimestamp = -1L;
//===========================