id生成算法-雪花算法(snowflake)及代码实现(代码片段)

、Dong 、Dong     2023-01-18     218

关键词:


前言

唯一ID可以标识数据的唯一性,在分布式系统中生成唯一ID的方案有很多,常见的方式大概有以下三种:

  • 依赖数据库,使用如MySQL自增列或Oracle序列等。
  • UUID随机数
  • snowflake雪花算法(本文将要讨论)

一、ID生成算法对比

  • 采用数据库自增序列
    读写分离时,只有主节点可以进行写操作,可能有单点故障的风险
    分表分库,数据迁移合并等比较麻烦
  • UUID随机数
    采用无意义字符串,没有排序
    UUID使用字符串形式存储,数据量大时查询效率比较低

二、雪花算法原理

SnowFlake算法生成id的结果是一个64bit大小的整数,它的结构如下图:

  • 其中1bit,不用,因为二进制中最高位是符号位,1表示负数,0表示正数。生成的id一般都是用整数,所以最高位固定为0。

  • 41bit-时间戳,用来记录时间戳,毫秒级。41位可以表示个数字,如果只用来表示正整数(计算机中正数包含0),可以表示的数值范围是:0 至 ,减1是因为可表示的数值范围是从0开始算的,而不是1。也就是说41位可以表示个毫秒的值,转化成单位年则是年

  • 10bit-工作机器id,用来记录工作机器id。可以部署在个节点,包括5位datacenterId和5位workerId

  • 5位(bit)可以表示的最大正整数是,即可以用0、1、2、3、…31这32个数字,来表示不同的datecenterId或workerId

  • 12bit-序列号,用来记录同毫秒内产生的不同id。12位(bit)可以表示的最大正整数是,即可以用0、1、2、3、…4094这4095个数字,来表示同一机器同一时间截(毫秒)内产生的4095个ID序号。由于在Java中64bit的整数是long类型,所以在Java中SnowFlake算法生成的id就是long来存储的。

SnowFlake可以保证所有生成的id按时间趋势递增整个分布式系统内不会产生重复id(因为有datacenterId和workerId来做区分)

三、java实现

public class IdWorker

    //下面两个每个5位,加起来就是10位的工作机器id
    private long workerId;    //工作id
    private long datacenterId;   //数据id
    //12位的序列号
    private long sequence;

    public IdWorker(long workerId, long datacenterId, long sequence)
        // sanity check for workerId
        if (workerId > maxWorkerId || workerId < 0) 
            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0",maxWorkerId));
        
        if (datacenterId > maxDatacenterId || datacenterId < 0) 
            throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0",maxDatacenterId));
        
        System.out.printf("worker starting. timestamp left shift %d, datacenter id bits %d, worker id bits %d, sequence bits %d, workerid %d",
                timestampLeftShift, datacenterIdBits, workerIdBits, sequenceBits, workerId);

        this.workerId = workerId;
        this.datacenterId = datacenterId;
        this.sequence = sequence;
    

    //初始时间戳
    private long twepoch = 1288834974657L;

    //长度为5位
    private long workerIdBits = 5L;
    private long datacenterIdBits = 5L;
    //最大值
    private long maxWorkerId = -1L ^ (-1L << workerIdBits);
    private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    //序列号id长度
    private long sequenceBits = 12L;
    //序列号最大值
    private long sequenceMask = -1L ^ (-1L << sequenceBits);
    
    //工作id需要左移的位数,12位
    private long workerIdShift = sequenceBits;
   //数据id需要左移位数 12+5=17位
    private long datacenterIdShift = sequenceBits + workerIdBits;
    //时间戳需要左移位数 12+5+5=22位
    private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
    
    //上次时间戳,初始值为负数
    private long lastTimestamp = -1L;

    public long getWorkerId()
        return workerId;
    

    public long getDatacenterId()
        return datacenterId;
    

    public long getTimestamp()
        return System.currentTimeMillis();
    

     //下一个ID生成算法
    public synchronized long nextId() 
        long timestamp = timeGen();

        //获取当前时间戳如果小于上次时间戳,则表示时间戳获取出现异常
        if (timestamp < lastTimestamp) 
            System.err.printf("clock is moving backwards.  Rejecting requests until %d.", lastTimestamp);
            throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds",
                    lastTimestamp - timestamp));
        

        //获取当前时间戳如果等于上次时间戳(同一毫秒内),则在序列号加一;否则序列号赋值为0,从0开始。
        if (lastTimestamp == timestamp) 
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) 
                timestamp = tilNextMillis(lastTimestamp);
            
         else 
            sequence = 0;
        
        
        //将上次时间戳值刷新
        lastTimestamp = timestamp;

        /**
          * 返回结果:
          * (timestamp - twepoch) << timestampLeftShift) 表示将时间戳减去初始时间戳,再左移相应位数
          * (datacenterId << datacenterIdShift) 表示将数据id左移相应位数
          * (workerId << workerIdShift) 表示将工作id左移相应位数
          * | 是按位或运算符,例如:x | y,只有当x,y都为0的时候结果才为0,其它情况结果都为1。
          * 因为个部分只有相应位上的值有意义,其它位上都是0,所以将各部分的值进行 | 运算就能得到最终拼接好的id
        */
        return ((timestamp - twepoch) << timestampLeftShift) |
                (datacenterId << datacenterIdShift) |
                (workerId << workerIdShift) |
                sequence;
    

    //获取时间戳,并与上次时间戳比较
    private long tilNextMillis(long lastTimestamp) 
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) 
            timestamp = timeGen();
        
        return timestamp;
    

    //获取系统时间戳
    private long timeGen()
        return System.currentTimeMillis();
    

    //---------------测试---------------
    public static void main(String[] args) 
        IdWorker worker = new IdWorker(1,1,1);
        for (int i = 0; i < 30; i++) 
            System.out.println(worker.nextId());
        
    



结尾

  • 感谢大家的耐心阅读,如有建议请私信或评论留言。
  • 如有收获,劳烦支持,关注、点赞、评论、收藏均可,博主会经常更新,与大家共同进步

java实现雪花算法(snowflake)-生成永不重复的id(源代码+工具类)使用案例(代码片段)

雪花算法是由Twitter公司开源的snowflake(雪花)算法。1、雪花算法的原理雪花算法会生成一个64位的二进制数据,为一个Long型。(转换成字符串后长度最多19),其基本结构:第一位:为未使用第二部分:41位为毫秒级时间(41位... 查看详情

小白知识:数据库表主键id生成策略及snowflake雪花算法的由来(代码片段)

早期单机早期单机系统习惯的主键有两种方式:整数的自增主键和字符串主键整数自增主键,数据库自己维护,每次+1,优点快速简单,具有顺序,方便排序,缺点高并发时会有主键冲突问题。... 查看详情

雪花算法原理解析(代码片段)

...戳)。美团Leaf算法(依赖于数据库,ZK)。  本文主要介绍SnowFlake算法,是Twitter开源的分布式id生成算法。  其核心思想就是: 查看详情

雪花算法snowflake分布式id生成原理详解,以及对解决时钟回拨问题几种方案讨论(代码片段)

文章目录一、前言二、雪花算法snowflake1、基本定义2、snowflake的优缺点三、Java代码实现snowflake1、组装生成id2、计算最大值的几种方式3、反解析ID4、ID生成器使用方式四、时钟回拨问题和解决方案讨论1、时间戳自增彻底解决时钟... 查看详情

id号生成雪花算法

参考技术A1、twitter的SnowFlake生成ID能够按照时间有序生成2、SnowFlake算法生成id的结果是一个64bit大小的整数3、分布式系统内不会产生重复id(用有datacenterId和machineId来做区分)datacenterId(分布式)(服务ID1,2,3.....)每个服务中... 查看详情

snowflake雪花算法分布式实现全局id生成(代码片段)

snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。这种方案大致来说是一种以划分命名空间(UUID也算,由于比较常见,所以单独分析)来生成ID的一种算法,这种方案把64-bit分别划分成多段,分开来标示机器、时间... 查看详情

雪花算法生成id(代码片段)

packagecom.shopping.test;/***SnowFlake的结构如下(每部分用-分开):<br>*0-00000000000000000000000000000000000000000-00000-00000-000000000000<br>*1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正... 查看详情

雪花算法如何生成用户id?有什么高明之处?(代码片段)

...介前言雪花算法生成用户ID分布式ID生成器分布式ID的特点snowflake算法介绍设计思想snowflake的Go实现Twitter索尼雪花 查看详情

分布式id生成方案:雪花算法(源自twitter)

参考技术A雪花(snowflake)在自然界中,是极具独特美丽,又变幻莫测的东西:雪花算法的原始版本是scala版,用于生成分布式ID(纯数字,时间顺序),订单编号等。算法描述:snowflake.gomain.go测试结果:结论: 查看详情

雪花算法(snowflake)delphi版(代码片段)

...位数,每毫秒可生成4096个ID用法:创建全局变量:snow:TDxSnowflake;创建对象:snow:=TDxSnowflake.Create;//不要忘了在退出时释放snow.Free;调用:snow.WorkerID:=100;mmo1.Lines.Add(FormatFloat(‘#0‘,snow.Generate));*)unitDxSnowflake;interfaceusesSystem.SysUtils,System... 查看详情

自增id算法snowflake(雪花)

  在数据库主键设计上,比较常见的方法是采用自增ID(1开始,每次加1)和生成GUID。生成GUID的方式虽然简单,但是由于采用的是无意义的字符串,推测会在数据量增大时造成访问过慢,在基础互联网的系统设计中都不推... 查看详情

分布式id理解snowflake算法的实现原理(代码片段)

1.概述转载:冷饭新炒:理解Snowflake算法的实现原理我上次也看了一个视频讲解:【分布式ID】键高并发分布式全局唯一ID雪花算法snowflake2.前提#Snowflake(雪花)是Twitter开源的高性能ID生成算法(服务)... 查看详情

分布式id生成方案-snowflake算法(代码片段)

...,欢迎关注。个人博客网站:分布式ID生成方案-snowflake算法背景在互联网的业务系统中,涉及到各种各样的ID,这些ID需要保证全局唯一。我们称之为分布式ID,分布式ID需要满足唯一性、趋势递增性、高可用性... 查看详情

分布式id生成方案-snowflake算法(代码片段)

...要满足唯一性、趋势递增性、高可用性、高性能等特点。snowflake算法,也叫雪花算法,是其中的一种分布式ID生成方案。是twitter公司内部分布式项目采用的ID生成算法,开源后广受国内大厂的好评ÿ 查看详情

雪花算法(代码片段)

雪花算法(snowflake):用于生成分布式ID(纯数字,时间顺序),订单编号等自增ID:记录可以根据ID号进行推测出来,对于数据敏感场景不宜使用。GUID:采用无意义字符串,数据量增大时造成访问过慢,且不宜排序。雪花算法描述... 查看详情

snowflake雪花算法唯一id

packagesnowflake;/***Twitter_Snowflake<br>*SnowFlake的结构如下(每部分用-分开):<br>*0-00000000000000000000000000000000000000000-00000-00000-000000000000<br>*1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数 查看详情

springboot中使用雪花算法生成雪花id(代码片段)

...目中使用雪花算法使用1、什么是雪花算法雪花算法(Snowflake)是一种生成全局唯一ID的算法,由Twitter公司开发。它可以在分布式系统中生成全局唯一的ID,解决分布式系统中的数据合并和分片等问题。雪花算法生... 查看详情

springboot分布式全局唯一id的生成-雪花算法snowflake

一背景描述1.1问题产生在分布式系统中,怎么使用全局唯一id?在分布式是,微服务的架构中,或者大数据分库分表中,多个不同节点怎么保持每台机器生成的主键id不重复,具有唯一性?方案1:mys... 查看详情