为了解决应用中需要生成不重复递增 ID 的场景。
常见的 id 生成策略有如下方案:
- 使用数据库自有策略生成自增 ID;
- 使用 uuid 或 guid;
- 读取当前毫秒数;
- Redis 生成 ID;
- 基于号段生成 ID;
- Twitter 的 snowflake 算法;
以上方案均有各自的优缺点。
本项目计划支持两种 ID 生成方案,分别是类雪花算法和基于号段步长的 ID 生成策略。
本方案选择使用数据库来作为 worker 分配和持久化工具。
类雪花算法策略支持如下功能:
- WorkerId 自动获取,支持 K8s、Docker 等容器环境。支持单节点和多节点的 ID 生成场景;
- 可自定义时间戳,worker 等号段长度,能够通过该方式支持较小数字的 ID 生成,支持 JS Number 数值不够场景;
- 支持时间回拨场景,本算法能够自动适应,但同一时间戳的回拨只能一次;
- 能够以模块方式集成在项目内部,避免远程调用性能损耗;
- 支持根据编号获取 ID,不同编号的 ID 序列互补影响;
- 支持预留位,支持扩展场景,如:
- 调整各位长度时,确保 ID 生成不重复;
- 更换其他 ID 生成器时,确保生成的 ID 不重复;
- 可以支持不同数据中心的 ID 标识;
本项目支持内置集成在服务内部,避免由于调用链路造成的性能损耗。也可以选择部署统一的 id 生成服务。
- 时间同步: 应用服务器和数据库服务器需要开启时间同步,以确保各服务器时间一致。
- 数据库: 用于获取 workId,目前支持 MySql,后续计划支持其他数据库。
目前雪花算法的定义如下:
1 bit | 41 bit | 5 bit | 5 bit | 12 bit |
---|---|---|---|---|
符号位 0 | 毫秒级时间 | 数据中心 id | workId | 毫秒内流水号 |
本项目类雪花算法模式,总长度也是 64 位,定义如下:
1 bit | [15 - 55] bit | [3 - 10] bit | 1 bit | 剩余长度 | [0 - 5] bit |
---|---|---|---|---|---|
符号位 0 | 时间戳 | workId | 时间回拨轮转位 | 时间戳内流水号 | 可选预留位 |
符号位: 固定为 0,确保生成为正数。
基于配置定义时间单位的时间戳,允许自定义长度,目前时间单位支持: 毫秒、秒、分钟、小时、天。
每种单位,限制时间戳位的最小值和最大值,定义如下:
毫秒 | 秒 | 分钟 | 小时 | 天 |
---|---|---|---|---|
[41, 55] | [31, 55] | [25, 50] | [19, 45] | [15, 40] |
如果希望生成的 ID 数值较小,那么可以考虑定义较长的时间戳位,但需要注意的是给流水号位留足阈值,避免同一时间戳超过最大流水号。
各时间单位位长度可使用时长,供参考:
毫秒
41 bit | 42 bit | 43 bit | 44 bit | 45 bit | 46 bit | 47 bit | 48 bit | 49 bit | 55 bit |
---|---|---|---|---|---|---|---|---|---|
69.7 年 | 139 年 | 278 年 | 557 年 | 1115 年 | 2231 年 | 4462 年 | 8925 年 | 17851 年 | 1142465 年 |
秒
31 bit | 32 bit | 33 bit | 34 bit | 35 bit | 36 bit | 37 bit | 38 bit | 39 bit | 55 bit |
---|---|---|---|---|---|---|---|---|---|
68 年 | 136 年 | 272 年 | 544 年 | 1089 年 | 2179 年 | 4358 年 | 8716 年 | 17432 年 | 1142465658 年 |
分钟
25 bit | 26 bit | 27 bit | 28 bit | 29 bit | 30 bit | 31 bit | 32 bit | 33 bit | 50 bit |
---|---|---|---|---|---|---|---|---|---|
63 年 | 127 年 | 255 年 | 510 年 | 1021 年 | 2042 年 | 4085 年 | 8171 年 | 16343 年 | 2142123110 年 |
小时
19 bit | 20 bit | 21 bit | 22 bit | 23 bit | 24 bit | 25 bit | 26 bit | 27 bit | 45 bit |
---|---|---|---|---|---|---|---|---|---|
59 年 | 119 年 | 239 年 | 478 年 | 957 年 | 1915 年 | 3830 年 | 7660 年 | 15321 年 | 4016480832 年 |
天
15 bit | 16 bit | 17 bit | 18 bit | 19 bit | 20 bit | 21 bit | 22 bit | 23 bit | 40 bit |
---|---|---|---|---|---|---|---|---|---|
89 年 | 179 年 | 359 年 | 718 年 | 1436 年 | 2872 年 | 5745 年 | 11491 年 | 22982 年 | 3012360624 年 |
用来定义存放 workerId 的长度,长度支持 3 - 10 bit。由于存在心跳时间占用,因此建议设置长度比实际工作节点大一倍,以供进行轮转。
工作节点各长度最大支持数量参考:
3 bit | 4 bit | 5 bit | 6 bit | 7 bit | 8 bit | 9 bit | 10 bit |
---|---|---|---|---|---|---|---|
(0-7)8 个节点 | (0-15)16 个节点 | 32 个节点 | 64 个节点 | 128 个节点 | 256 个节点 | 512 个节点 | 1024 个节点 |
长度为 1bit,用于当时间回拨时轮转,默认为 0。例如当时间回拨时,该位会从 0 置为 1,当时间再次回拨时,该位将从 1 再次置为 0。
需要注意的是,由于策略限制,同一时刻不允许回拨两次。
流水号位用于同一时间戳下的 id 获取流水。请根据使用场景预留好流水号长度。
流水号位的长度
= 64
- 1(符号位)
- 时间戳位数
- workerId位数
- 1(时间回拨轮转位)
- 1(预留位)
各位数支持数量参考 workerId 表格。
该预留位是可选的,如果不需要可以设置长度为0
,长度范围支持 0-5
bit。建议设置长度为 1
bit 值为 0
。可以支持多种用途,例如:
- 调整各位长度时,可以将该位置为 1,以确保 ID 生成不重复;
- 更换其他 ID 生成器时,可以将该位强制置为 1,以确保生成的 ID 不重复;
- 可以支持不同数据中心的 ID 标识;
IdMode
: Id 生成模式,Snowflake
: 雪花算法;NumberSection
: 号段模式,目前仅支持Snowflake
,必填;DbConfig
: 数据库配置,必填;dbType
: 数据库类型,mysql
、postgresql
,目前仅支持mysql
;dbUrl
: 数据库连接,格式:jdbc:mysql://{host}:{port}/{dbName}?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=UTC
;dbName
: 数据库用户名;dbPassword
: 数据库密码;tableName
: 工作节点表名,默认为:soc_raindrop_worker
;
Logger
: 日志,非必填;ServicePort
: 服务监听端口,非必填;PriorityEqualCodeWorkId
: 优先相同 code 的 workerId(毫秒,秒单位场景下生效),默认:false
。code 格式为:{内网 ip}:{ServicePort}#{Mac 地址}
;TimeUnit
: 时间戳单位,必填;- 1: 毫秒(可能会有闰秒问题);
- 2: 秒,默认;
- 3: 分钟;
- 4: 小时,间隔过大不建议选择;
- 5: 天,间隔过大不建议选择;
StartTimeStamp
: 起始时间,时间戳从该时间开始计时,必填;timeStampLength
: 时间戳位数,请根据不同时间单位设置合理长度,必填;workIdLength
: 工作节点 id 长度,取值范围 3 - 10 位,必填;ServiceMinWorkId
: 服务的最小工作节点 id,默认 1,需在 workIdLength 的定义范围内,最大值最小值用于不同数据中心的隔离。ServiceMaxWorkId
: 服务的最大工作节点 id,默认 workIdLength 的最大值,需在 workIdLength 的定义范围内。TimeBackBitValue
: 时间回拨位初始值,支持0
或1
,默认:0
;EndBitsLength
: 可选预留位长度,支持0
-5
, 如果不需要可以设置为0
, 建议设置为1
EndBitsValue
: 最后预留位的值,设置固定值,默认:0
;
- 由于
流水号位的长度
=64
-1(符号位)
-时间戳位数
-workerId位数
-1(时间回拨轮转位)
-1(预留位)
,因此在设置的时候需要评估在时间区间内是否存在流水号用尽的情况。 ServiceMinWorkId
和ServiceMaxWorkId
区间数量建议设置为服务节点数的两倍,以供PriorityEqualCodeWorkId
为false
时可能的重启后轮转。- 项目第一次启动时会判断依赖的表是否存在,如果不存在会自动创建表,同时根据
ServiceMinWorkId
和ServiceMaxWorkId
初始化数据。如果表已存在则不会进行初始化。项目运行过程中不会主动创建新的 worker 信息。
Js 表示数字的最大值为:9007199254740992,即 2 的 53 次方。
为了避免生成的 ID 在一定时间范围内超过该数值,那么可以考虑扩大 TimeStampLength
的值,比较合理的设置为 TimeStampLength - (63 - 53)
的位数长度仍然能维持较长时间的 ID 生成。因此如果时间单位是毫秒时TimeStampLength
建议至少定义为 50
,如果时间单位是秒时 TimeStampLength
建议至少定义为 40
。
[TODO]