文章目录
前言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个思路,来自一个资深架构师的总结 - 王延炯
相关链接
发表评论