ID生成器的特性
- 唯一性
- 时间相关
- 粗略有序
- 可反解
- 可制造
发号器(统一中央发号器)
为了避免频繁请求获取ID,则每一次请求都请求一批ID,留着备用,所以此时ID是准连续的,不同机器之间通过多个高位bit来区分
例如总共ID16位,则每次请求都将获取1000 0000(128)
一共有8个机器,则高3位用来区分不同机器,比如机器A分配的高位为001,则每一请求10000000(2的7次方个128个ID),则取走的范围为:0010 0000 0000 0000 ~ 0010 0000 0111 1111,下一次取走
0010 0000 1000 0000 ~ 0010 0000 1111 1111
UUID本地生成
UUID 128位
实际上,UUID一共有多种算法,能用于TraceId的是:
- version1: 基于时间的算法
然后是Version1,严格守着原来各个位的规矩:
因为时间戳有满满的60bit,所以可以尽情花,以100纳秒为1,从1582年10月15日算起(能撑3655年,真是位数多给烧的,1582年有意思么)
节点标识也有48bit,一般用MAC地址表达,如果有多块网卡就随便用一块。如果没网卡,就用随机数凑数,或者拿一堆尽量多的其他的信息,比如主机名什么的,拼在一起再hash一把。
顺序号这16bit则仅用于避免前面的节点标示改变(如网卡改了),时钟系统出问题(如重启后时钟快了慢了),让它随机一下避免重复。
但好像Version 1就没考虑过一台机器上起了两个进程这类的问题,也没考虑相同时间戳的并发问题,所以严格的Version1没人实现,接着往下看各个变种吧。
- version4: 基于随机数的算法
先说Version4,这是最暴力的做法,也是JDK里的算法,不管原来各个位的含义了,除了少数几个位必须按规范填,其余全部用随机数表达。
JDK里的实现,用 SecureRandom生成了16个随机的Byte,用2个long来存储。记得加-Djava.security.egd=file:/dev/./urandom,否则会锁住程序等噪音。
本地64位ID生成
划分命名空间本地并行生成
将机器MAC地址和进程号进行Hash作为高位,然后时间戳(秒级别)作为中位,然后同一时间(1s内)内的自增序列
所以(20-24bit)机器Hash + 时间戳(24-25bit)+ (16-17bit自增序列)