分布式ID生成算法

分布式id生成算法之:雪花算法

雪花算法概述

雪花算法是由4个部分组合而成的:符号位+41位时间戳+10位机器码+12位序列号。

代码块
 
         
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">   0 - 00000000 00000000 00000000 00000000 0 - 00000000 00 - 00000000 0000</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000"> 1bit不用         中间41bit是时间戳               10bit是机器码    12bit为序列号</span></span></span></span>

这么组合的好处是:

  • 全局唯一:在分布式 部署的环境下,相同机器上,不同机器之间,不能出现重复ID。

  • 数据安全:如果涉及到如订单号类的ID透出诉求,则需要考虑用非连续ID来隐藏生产状况。

源码分析

定义ID组合方式所需的常量
 
         
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">/* 开始时间 (2020-05-03) */</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">private final long twepoch = 1607529600000L;</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">​</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">/* 机器码所占的bit位数 = 10位 */</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">private final long workerIdBits = 10L;</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">​</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">/* 最大支持的机器码 = 1023,该式子相当与~(-1L << 10L) */ </span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">private final long maxWorkerId = -1L ^ (-1L << workerIdBits);</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">​</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">/* 序列号所占的bit位数 */</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">private final long sequenceBits = 12L;</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">​</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">/* 机器码需要在序列号的左边 */</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">private final long workerIdShift = sequenceBits;</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">​</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">/* 时间戳的起始位置在序列号和机器码的左边 */</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">private final long timestampLeftShift = sequenceBits + workerIdBits;</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">​</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">/* 序列号的最大支持数值 */</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">private final long sequenceMask = -1L ^ (-1L << sequenceBits);</span></span></span></span>
定义三个位段的含义字段
 
         
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">/* 机器码 (0 ~ 1023) */</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">private long workerId;</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">​</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">/* 序列号 (0 ~ 4095) */</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">private long sequence = 0L;</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">​</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">/* 最后时间戳 */</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">private long lastTimestamp = -1L;</span></span></span></span>
构建机器码
 
         
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">InetAddress address;</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">try {</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">    //获取本机IP</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">    address = InetAddress.getLocalHost();</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">} catch (final UnknownHostException e) {</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">    throw new IllegalStateException("Cannot get LocalHost InetAddress, please check your network!",e);</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">}</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">byte[] ipAddressByteArray = address.getAddress();</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">//机器码一共需要10位,所以取段倒数第二个段取最后2位 + 倒数第一段全部8位</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">return ((ipAddressByteArray[ipAddressByteArray.length - 2] & 0B11) << Byte.SIZE) + (ipAddressByteArray[ipAddressByteArray.length - 1] & 0xFF);</span></span></span></span>
代码块
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">//当前时间戳</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">long timestamp = System.currentTimeMillis()</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">//如果当前时间戳 < 最后记录时间戳 ,则可能发生时钟回拨,异常中断</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">if (timestamp < lastTimestamp) {</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">    throw new RuntimeException(String.format(</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">        "clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">}</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">//如果当前时间戳 == 最后记录时间戳 </span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">if (lastTimestamp == timestamp) {</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">    //计算下一个序列号,这里&4095的作用是循环,因为到4096会变成0</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">    sequence = (sequence + 1) & sequenceMask;</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">    //如果下一个序列号==0,则说明同一时间戳内序列号以用完,设置下一时间戳为最后时间戳</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">    if (sequence == 0) {</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">        //该方法内部while循环直到当前时间>最后时间   </span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">        timestamp = tilNextMillis(lastTimestamp);</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">    }</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">} else {</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">    sequence = 0L;</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">}</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">lastTimestamp = timestamp;</span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">//时间戳和指定时间的差 左移 12+10 | 机器码 左移12  | 序列号 </span></span></span></span>
<span style="background-color:#fcfcfc"><span style="color:#333333"><span style="background-color:#fcfcfc"><span style="color:#000000">return ((timestamp - twepoch) << timestampLeftShift) | (workerId << workerIdShift) | sequence;</span></span></span></span>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

博士通

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值