首先要说一下什么叫Snowflak

雪花算法(Snowflake Algorithm)是一种分布式ID生成算法,最初由Twitter开发并使用,用于在分布式系统中生成全局唯一且有序的64位整数ID。这种算法生成的ID通常称为雪花ID(Snowflake ID)。

主要分几个部分

  1. 时间戳:获取当前时间戳,减去起始时间戳,得到一个相对时间戳。这个相对时间戳占用41位。

  2. 工作机器ID:每个服务器有一个唯一的ID,这个ID需要事先配置好,并且保证在一个集群中是唯一的。这个ID占用10位,最多支持1024个不同的服务器。

  3. 序列号:对于同一个毫秒内的多个请求,使用序列号来区分,序列号每增加一次就加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 可以大概用六十多年

位数

可表示毫秒数

大概能运行多久

37位

137,438,953,471 ms

4.36 年

41位

2,199,023,255,551 ms

69.7 年

举例

首先要明白 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