文章目录

前言MongoDB ObjectIdTwitter SnowflakeUUID

前言

基于数据库设置其实初始值,以及增量步长。基于ZK,Redis,改良雪花集中式服务生成,远程调用获取id。基于并行空间划分,Snowflake(8Byte字节64bit位),MongoDB ObjectId(12字节),UUID(16字节)基于全随机UUID(16字节)

MongoDB ObjectId

12-byte MongoDB ObjectId 的结构是:

a 4-byte value representing the seconds since the Unix epoch,.a 3-byte machine identifier.a 2-byte process id.a 3-byte counter, starting with a random value.

可以看出,这个方案所支持的最小划分粒度是「秒 * 进程实例」,单进程实例的每秒容量是 3-byte (24-bit),也就是接近16777216个ID。

即每个实例每秒1600多W。

看代码(MonogoDB 3.3.x Java Driver)

1. Timestamp

2. Machine Identifier

3. Process ID

4. COUNTER

此处需要注意的是MongoDB的 NEXT_COUNTER 其初始值是一个随机数,这是有利于分库分表的。因为在小并发的条件下,非随机数的初始值,容易产生 偏库偏表, 不均匀的现象。

Twitter Snowflake

64-bit 长的 Snowflake ,它的结构是:

1-bit reserved41-bit timestamp millisecond10-bit machine id12-bit sequence

所支持的最小划分粒度是「毫秒 * 线程」,单线程(Snowflake 里对应的概念是 Worker)的每毫秒容量是12-bit,也就是接近4096。

即每个实例每秒4096000(400w)

初始化时间自己指定,例如:

// Thu, 04 Nov 2010 01:42:54 GMT

twepoch = 1288834974657L;

1. Timestamp

System.currentTimeMillis() - twepoch

2. Machine Identifier

同上

3. Process ID

同上

4. Sequence

if (lastTimestamp == timestamp) {

// 相同毫秒内,序列号自增

sequence = (sequence + 1) & sequenceMask;

if (sequence == 0) {

// 同一毫秒的序列数已经达到最大, 则等待下一个毫秒时间,即使用下一秒 sequence默认0L

timestamp = tilNextMillis(lastTimestamp);

}

} else {

// 不同毫秒内,序列号置为 [1 - 3) 随机数 即 1或者2 防止每毫秒初始值相同

sequence = ThreadLocalRandom.current().nextLong(1, 3);

}

sequence初始值是一个[1 - 3) 随机数,这是有利于分库分表的。因为在小并发的条件下,非随机数的初始值,容易产生 偏库偏表, 不均匀的现象。

闰秒问题以及运行中时钟回拨问题

闰秒会出现把协调世界时向前拨1秒(负闰秒,最后一分钟为59秒)或向后拨1秒(正闰秒,最后一分钟为61秒);

// 当前时间小于上次时间,即出现闰秒或者时钟回溯了

if (timestamp < lastTimestamp) {

long offset = lastTimestamp - timestamp;

if (offset <= 5) { // 回溯时差 如果小于等于 5 ms

try {

wait(offset << 1); //等个2倍时间 再获取时间戳

timestamp = timeGen();

if (timestamp < lastTimestamp) { // 还小则抛出异常

throw new RuntimeException("时钟回调了 拉了裤");

}

} catch (Exception e) {

throw new RuntimeException(e);

}

} else {

throw new RuntimeException("时钟回调了 拉了裤");

}

}

延迟等待解决,实际应用项目: 美团的leaf,。

UUID

参见另一篇:UUID的5个版本

版本1 - UUID 是根据时间和 节点ID(通常是MAC地址)生成;时间+MAC,不安全 版本2 - UUID是根据标识符(通常是组或用户ID)、时间和节点ID生成; 版本3、版本5 - 确定性UUID 通过散列(hashing)名字空间(namespace)标识符和名称生成; 版本4 - UUID 使用随机性或伪随机性生成。

一般的,使用的最多的是UUID Version 4,很大程度上是因为其依赖的其他服务最少。

Java只支持生成版本3和版本4的UUID

V4 随机UUID,UUID.randomUUID().toString()

V4 随机UUID重复概率问题?

V4随机版本有一定的重复概率但是极低,直接上结论,来着维基百科

在 103 万亿 个 版本4 UUID 中找到重复的概率是(十亿分之一)。

参考:

生成全局唯一ID的3个思路,来自一个资深架构师的总结 - 王延炯

相关链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: