背景

实际业务中,往往会用数据库自增ID来作为业务对象的唯一ID;
但数据日渐增多的互联网行业,分库分表则成了行业的通用解决方案;
此时数据库自增ID就不能满足业务需求了,行业内有各种分布式ID生成解决方案,其中雪花ID就是其中使用较多的一种。

雪花算法原理

1 基本构成

雪花算法(Snowflake)是 Twitter 开源的分布式 ID 生成算法,可以基于时间生成全局不重复的、有序的、可自增的 64 Bit 的 ID,适用于分布式系统中的 ID 生成需求。
在标准版本中由以下部分组成:
符号位(1bit)- 时间戳相对值(41bit)- 数据标志(5bit)- 机器标志(5bit)- 递增序号(12bit)
0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
一起来一共64Bit组成,其中:
1、最高位是符号位,用于区分是正数还是负数,这里始终为0,我们用不到;
2、41位的毫秒级时间戳,41位的长度可以使用69年;
3、5位datacenterId和5位workerId,加起来共10位,最多支持32 x 32 = 1024个节点
4、最后12位是毫秒内的计数,12位的计数顺序号支持每个节点每毫秒产生4096个ID序号

2 生成ID原理

标准版雪花算法在实例化时,由于支持的时间范围有限制,所以开始的时候需要指定一个开始时间,并指定数据标识和机器标识;
如设定开始时间为 2020-01-01 00:00:00,数据标识设置为:15,机器标识设置为18;
此时我们想要生成1个雪花ID
1、获取时间戳差值
先获取当前时间戳与设定的开始时间的差值
当前时间2023-09-16 17:00:00转换为时间戳:1694854800000
开始时间2020-01-01 00:00:00转换为时间戳:1577808000000
计算时间戳差值:1694854800000 - 1577808000000 = 117046800000
转换为二级制:1101101000000100010000110111010000000
2、获取数据标识和机器标识
数据标识:15;机器标识:18
分别转换为二级制:数据标识:1111;机器标识:10010
3、获取毫秒内计数
毫秒内计数即是同一毫秒内,获取的第n个雪花ID,假设我们获取的是第1024个雪花ID
1024换算为二级制:10000000000
则生成的雪花ID为:时间戳差值+数据标识+机器标识+毫秒内计数
1101101000000100010000110111010000000 1111 10010 10000000000
二进制转为十进制,生成的雪花ID即为:122732465357820928 - 共18位

雪花算法优缺点

1 优点

  • 唯一性:雪花算法生成的ID是全局唯一的,可以在分布式系统中生成不重复的业务ID;
  • 有序性:基于时间戳的特性,使得雪花算法生成的ID是顺序增长的;
  • 高性能:雪花算法的生成过程是基于位运算实现的,性能好,并且标准的雪花算法每毫秒支持4096个ID生成,满足绝大多数的业务场景;
  • 独立性:雪花算法不依赖中央系统或数据库,非常方便在业务中落地和进行横向水平扩展;

2 缺点

  • 依赖系统时钟:雪花算法依赖系统时钟,如果时钟发生回退,会导致ID生成重复,当然这个都有对应的解决方案;
  • 有限的容量:标准的雪花算法支持每毫秒生成4096个ID,如果超过了容量限制,则需要等待下一毫秒才可以生成新的ID;
  • 前端精度丢失:标准雪花算法生成的ID在18位和19位之间(时间差超过7.56年,就会达到19位),JavaScript 的 number 类型的数值范围是:2的53次方减1,所以数字位数大于16位且大于 9007199254740991 的数,进制转换会存在精度问题,而雪花ID生成的数值过大,导致超出JavaScript的精度范围,无法直接在前端展示,使用字符串展示雪花ID可避免此问题。

定制16位雪花ID

1 雪花算法定制

既然JavaScript只支持16位一下的数字展示,那我们生成16位以下的雪花ID即可;
标准的雪花算法为64Bit,每个位置的设计冗余量都很大,业务中完全可以根据自己的业务形态去定制雪花算法;
1、缩短支持时间
标准雪花算法为41位时间戳差值,支持使用69年,实际业务中很少有能跑69年的业务,我们可以将时间戳差值修改为39位,39位时间戳支持使用:(2^39)/(1000606024365) = 17.43 年,17年足够大部分业务使用;
2、只保留机器标识
标准雪花算法支持:5位的数据标识和5位的机器标识
即最大支持 32 x 32 = 1024 节点部署
实际业务很少有这么大规模的机器部署,一般最多也就10台机器,这里完全可以将5位数据标识去除,只保留5位机器标识,即支持32个节点部署;
3、缩短毫秒内计数
标准雪花算法同一毫秒支持12位的序列,即每毫秒支持4096个ID生成
实际业务也很少能用到这么多ID,我们可以将序列缩短为8位,即每毫秒支持256个ID生成,完全满足大部分业务需要;
经过以上定制的雪花算法,最多支持 32 台机器,每台机器每毫秒能够生成最多 256 个 ID,整个集群理论上每秒可以生成 32 1000 256 = 800万 个ID;

2 最大支持ID

39位时间戳,最大值:549755813887毫秒,约17.43264250022197年
5位机器号,支持最大机器数量:32
8位序列,每毫秒生成:256个ID,每秒生成:256000个ID
组成的最大二进制:1111111111111111111111111111111111111111111111111111 共52位
最大生成ID:4503599627370495 - 共16位

经过定制的雪花算法,完全满足大部分业务使用。

源码

源码发布于:moyu-framework
详见 Github:https://github.com/MoYu-Group/moyu-framework/blob/main/moyu-base/moyu-util/src/main/java/io/github/moyugroup/base/util/UUIDUtil.java