雪花算法生成分布式唯一ID

导读:本篇文章讲解 雪花算法生成分布式唯一ID,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,原文地址:Java

也许你感觉自己的努力总是徒劳无功,但不必怀疑,你每天都离顶点更进一步。今天的你离顶点还遥遥无期。但你通过今天的努力,积蓄了明天勇攀高峰的力量。加油!

什么是雪花算法

雪花算法(Snowflake)是一种生成分布式全局唯一ID的算法,生成的ID称为Snowflake IDs或snowflakes。这种算法由Twitter创建,并用于推文的ID。Discord和Instagram等其他公司采用了修改后的版本。

一个Snowflake ID有64位元。前41位是时间戳,表示了自选定的时期以来的毫秒数。 接下来的10位代表计算机ID,防止冲突。 其余12位代表每台机器上生成ID的序列号,这允许在同一毫秒内创建多个Snowflake ID。SnowflakeID基于时间生成,故可以按时间排序。 此外,一个ID的生成时间可以由其自身推断出来,反之亦然。该特性可以用于按时间筛选ID,以及与之联系的对象。

上述解释来源于wiki百科。

雪花算法实现思路

从上面的解释中可以知道,雪花算法依靠时间戳,序列号等来保证唯一性。

这里我们使用java中long类型64位来当做唯一id,然后看下这64是怎么分配的。

1 41 12
最高位为符号位 时间戳 序列号

按照上面这种分配方式使用了54位,还剩了10位没使用。而且我们还发现了一个问题
Q:按照这种方式,如果部署多台不是会重复吗?
那么按照思路,我们只需要将剩下的10位当做部署的机器码,那么这个问题就可以解决了,当然有一些还需要区分下应用类型。

1 41 5 5 12
最高位为符号位 时间戳 workspace 机器码 序列号

这样就保证了多台部署问题和不同应用有一个区分度(根据workspace来区分),到这数据位数的含义就分配完毕了,下面就是生成的流程了

  1. 获取当前时间timestamp和上一次获取id的事件lastTimestamp
  2. 判断时间
    1. 如果timestamp < lastTimestamp,那么抛出错误(因为后续时间必定大于之前的,不然无法保证id不重复,而出现这种情况可能是时钟回拨)
    1. 如果timestamp==lastTimeStamp,那么自增序列sequence+1,然后与其上限值比较
      1. 如果没超过上限值,那么继续向下执行
      1. 如果超过了上限值,自旋获取时间,直到获得下一个时间,然后设置sequence为0
  3. 如果timestamp>lastTimestamp,这种情况是最安全的,那么直接设置sequence为0
  4. 更新lastTimestamp
  5. 返回按位组装的long值

雪花算法的java实现

/**
 * @Author:TangFenQi
 * @Date:2021/11/4 11:43
 */
public class IdWorker {

    private static final int TIMESTAMP_BIT=41;//时间戳位数
    private static final int WORKSPACE_BIT=5;//工作空间位数
    private static final int MACHINE_BIT=5;//机器码位数
    private static final int SEQUENCE_BIT=12;//自增序列位数

    private static final int WORKSPACE_MASK=(1<<WORKSPACE_BIT)-1;//工作空间上界
    private static final int MACHINE_MASK=(1<<MACHINE_BIT)-1;//机器码上界
    private static final int SEQUENCE_MASK=(1<<SEQUENCE_BIT)-1; //自增寻列上界

    private static long lastTimestamp=-1; //记录上一次生成id时的时间戳
    private static long sequence=0;

    public static synchronized long nextId(int workspace,int machineId){
        Assert.isTrue(workspace>=0,String.format("workspace only be positive ! workspace[%n]",workspace));
        Assert.isTrue(workspace<=WORKSPACE_MASK,String.format("workspace number out of bound ! workspace:[%n],limit:[%n]",workspace,WORKSPACE_MASK));
        Assert.isTrue(machineId>=0,String.format("workspace only be positive ! machineId[%n]",workspace));
        Assert.isTrue(machineId<=MACHINE_MASK,String.format("workspace number out of bound ! machineId:[%n],limit:[%n]",workspace,MACHINE_MASK));
        //获取当前时间戳
        long timestamp = System.currentTimeMillis();
        if(timestamp<lastTimestamp){
            //比较是否大于上一次时间(防止时钟回拨导致生成重复ID)
            throw new IllegalArgumentException(String.format("lastTimestamp later then current time! pls check whether turn the clock?? current timestamp [%s] ,last timestamp [%s] ",timestamp,lastTimestamp));
        }else if(timestamp==lastTimestamp){
            //是否与上一次时间戳相等(需要分情况,可能出现向后借时间的问题)
            sequence=(sequence+1)&SEQUENCE_MASK;
            //如果越界
            if(sequence==0){
                timestamp=getNextTime(timestamp);
            }
        }else {
            //是否大于上一次时间戳(安全)
            sequence=0;
        }

        lastTimestamp=timestamp;
        return timestamp<<(WORKSPACE_BIT+MACHINE_BIT+SEQUENCE_BIT)|
                workspace<<(MACHINE_BIT+SEQUENCE_BIT)|
                machineId<<(SEQUENCE_BIT)|
                sequence;
    }

    private static long getNextTime(long currentTimestamp){
        long newTimestamp=System.currentTimeMillis();
        while (newTimestamp<=currentTimestamp){
            newTimestamp=System.currentTimeMillis();
        }
        return newTimestamp;
    }


Twitter雪花算法的实现地址,不过使用的是scala。

上述代码的实现地址:github

代码无涯,与君共勉。

喜欢的同学,不介意的话给我点个star吧 (* ̄︶ ̄)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/121863.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!