简化版本的雪花SnowflakID
首先要说一下什么叫Snowflak
雪花算法(Snowflake Algorithm)是一种分布式ID生成算法,最初由Twitter开发并使用,用于在分布式系统中生成全局唯一且有序的64位整数ID。这种算法生成的ID通常称为雪花ID(Snowflake ID)。
主要分几个部分
时间戳:获取当前时间戳,减去起始时间戳,得到一个相对时间戳。这个相对时间戳占用41位。
工作机器ID:每个服务器有一个唯一的ID,这个ID需要事先配置好,并且保证在一个集群中是唯一的。这个ID占用10位,最多支持1024个不同的服务器。
序列号:对于同一个毫秒内的多个请求,使用序列号来区分,序列号每增加一次就加1,直到达到最大值12位全满(即4096),然后重置为0,等待下一个毫秒到来。
简化版代码
import java.util.concurrent.atomic.AtomicLong
class SequenceIdGenerator : IdGenerator<Long> {
private val sequence = AtomicLong()
private val timestamp get() = System.currentTimeMillis() - BASE_TIMESTAMP and 0x1FFFFFFFFFL
private val host = hashCode().toLong()
get() = field and 0x3B
private fun getSequence() = sequence.getAndIncrement() and 0x3FF
override fun generate() = timestamp shl 16 or (host shl 10) or getSequence()
companion object {
private const val BASE_TIMESTAMP = 0x176B986FC00L
val INSTANCE = SequenceIdGenerator()
@JvmStatic
fun generateId(): Long {
return INSTANCE.generate()
}
}
}
解释一下各部分
private const val BASE_TIMESTAMP = 0x176B986FC00L
首先定义一个基准时间,这个是2021-01-01 00:00:00时间戳(1609430400000)转成了16进制。
private val timestamp get() = System.currentTimeMillis() - BASE_TIMESTAMP and 0x1FFFFFFFFFL
当前时间和 BASE_TIMESTAMP 计算的毫秒数,保留低 37 位。
private val host = hashCode().toLong()
get() = field and 0x3B
主机标识(host):对象哈希值取低 6 位
private fun getSequence() = sequence.getAndIncrement() and 0x3FF
序列号(sequence):同一毫秒内的递增序号,保留低 10 位。(若在同一毫秒内调用次数超过 1024(2^10),会出现重复序列号问题。)
override fun generate() = timestamp shl 16 or (host shl 10) or getSequence()
timestamp shl 16:将时间戳左移 16 位,为后续的主机标识和序列号腾出空间。
host shl 10:将主机标识左移 10 位,为序列号腾出空间。
getSequence():获取当前序列号,保留低 10 位。
or 操作:将三部分按位拼接成一个 64 位的 Long 值。
截取
0x1FFFFFFFFFL
转成2进制是:1111111111111111111111111111111111111 长度37 所以毫秒差值只 保留37位。这个长度可以用大概4年多。
如果改成0x1FFFFFFFFFFL
可以大概用六十多年
举例
首先要明白 and 预算里面的0和1相当于true 和false 同一个位数上的相比 1 & 1 = 1, 1 & 0 =0, 0 & 0 = 0.
假设现在的毫秒差是:137774438523。
137774438523 and 0x1FFFFFFFFFFL 等价于
10000000010011111111110001100001111011 and 11111111111111111111111111111111111111111
结果是:
00010000000010011111111110001100001111011 (十进制结果是137774438523)
再经过左移预算:
00010000000010011111111110001100001111011 shl 16
结果是:
000100000000100111111111100011000011110110000000000000000 (十进制结果是9029185603043328)
在拼上机器码ID, 假设是 35 and 0x3B ->
100011 & 111011
结果是 100011 (十进制结果是35)
所以最后的结果是
000100000000100111111111100011000011110111000110000000000
9029185603079168