雪花算法&改造16位或者15位

这篇具有很好参考价值的文章主要介绍了雪花算法&改造16位或者15位。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、默认版本-64bit

雪花算法原理图:
b060547d6531373e4b4b98c6b7f1ea89.png
使用1位作为符号位,确定为0, 表示正
使用41位作为毫秒数
使用10位作为机器的ID : 高5位是数据中心ID, 低5位是机器ID
使用12位作为毫秒内的序列号,意味着每个节点每秒可以产生4096(212)个ID;该算法通过二进制的操作进行实现,单机每秒内理论上最多可以生成1000*(2^12),即409.6万个ID。

/**
 * Twitter_Snowflake<br>
 * SnowFlake的结构如下(每部分用-分开):<br>
 * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 <br>
 * 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0<br>
 * 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)
 * 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69<br>
 * 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId<br>
 * 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号<br>
 * 加起来刚好64位,为一个Long型。<br>
 * SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。
 */
public class SnowflakeIdWorker {

    // ==============================Fields===========================================
    /** 开始时间截 (2015-01-01) */
    private final long twepoch = 1420041600000L;

    /** 机器id所占的位数 */
    private final long workerIdBits = 5L;

    /** 数据标识id所占的位数 */
    private final long datacenterIdBits = 5L;

    /** 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);

    /** 支持的最大数据标识id,结果是31 */
    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);

    /** 序列在id中占的位数 */
    private final long sequenceBits = 12L;

    /** 机器ID向左移12位 */
    private final long workerIdShift = sequenceBits;

    /** 数据标识id向左移17位(12+5) */
    private final long datacenterIdShift = sequenceBits + workerIdBits;

    /** 时间截向左移22位(5+5+12) */
    private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

    /** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) */
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);

    /** 工作机器ID(0~31) */
    private long workerId;

    /** 数据中心ID(0~31) */
    private long datacenterId;

    /** 毫秒内序列(0~4095) */
    private long sequence = 0L;

    /** 上次生成ID的时间截 */
    private long lastTimestamp = -1L;

    //==============================Constructors=====================================
    /**
     * 构造函数
     * @param workerId 工作ID (0~31)
     * @param datacenterId 数据中心ID (0~31)
     */
    public SnowflakeIdWorker(long workerId, long datacenterId) {
        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));
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }

    // ==============================Methods==========================================
    /**
     * 获得下一个ID (该方法是线程安全的)
     * @return SnowflakeId
     */
    public synchronized long nextId() {
        long timestamp = timeGen();

        //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(
                    String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }

        //如果是同一时间生成的,则进行毫秒内序列
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            //毫秒内序列溢出
            if (sequence == 0) {
                //阻塞到下一个毫秒,获得新的时间戳
                timestamp = tilNextMillis(lastTimestamp);
            }
        }
        //时间戳改变,毫秒内序列重置
        else {
            sequence = 0L;
        }

        //上次生成ID的时间截
        lastTimestamp = timestamp;

        //移位并通过或运算拼到一起组成64位的ID
        return ((timestamp - twepoch) << timestampLeftShift) //
                | (datacenterId << datacenterIdShift) //
                | (workerId << workerIdShift) //
                | sequence;
    }

    /**
     * 阻塞到下一个毫秒,直到获得新的时间戳
     * @param lastTimestamp 上次生成ID的时间截
     * @return 当前时间戳
     */
    protected long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    /**
     * 返回以毫秒为单位的当前时间
     * @return 当前时间(毫秒)
     */
    protected long timeGen() {
        return System.currentTimeMillis();
    }

    //==============================Test=============================================
    /** 测试 */
    public static void main(String[] args) {
        SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 0);
        for (int i = 0; i < 1000; i++) {
            long id = idWorker.nextId();
            System.out.println(Long.toBinaryString(id));
            System.out.println(id);
        }
    }
}

代码原理

其中有对三个部分都限制了最大值(MAX_DATA_CENTER_NUM、MAX_MACHINE_NUM、MAX_SEQUENCE),我们通过图解的方式来看下计算过程:
image.png
image.png

image.png

范围:

image.png
我们通过计算器,我们可以看出来,默认版本的雪花算法,最大数值长度:19位,最大值:9223372036854775807

优点

  1. 按照时间自增排序,在多个分布式系统内不会产生id碰撞(数据中心+机器id区分)

  2. 高性能:理论上QPS约为409.6w/s(1000*2^12)

  3. 不依赖于任何外部第三方系统

  4. 灵活性高:可以根据自身业务情况调整分配bit位

    缺点

  5. 强依赖时钟:生成都是以时间自增,如果时间回拨,可能导致id重复

  6. 返给前端时,需要将lang类型id转换成string类型,因为前端JavaScript的number类型最大接收16位长度数值,而雪花算法获得的id最大长度为19位。

美团对此缺点做了一些改进,具体可以参考:Leaf——美团点评分布式ID生成系统

二、修改版本一:32bit

image.png
大致与64bit相同,唯一区别是时间戳部分这里仅占用32bit,因为保存的时间戳为:当前时间戳-雪花算法开始的时间戳,得出来的数据仅用10bit就可以保存,位数越少,对磁盘、数据索引等数据提高越明显

三、修改版本二:生成15位的id

在有些时候,我们的系统并发并没有很大,同时也不会部署2^5的数据中心和2^5的机器id。
因为并发量用不到那么高,我们可以将12位序列位缩短。
比如:不区分数据中心,机器id最多2^ 3=8 台,每毫秒最大并发为2^5=256。将64位版本的雪花算法改造一下:
image.png
这样的设计,只有41位用来存储时间戳,3位用来存储机器id,5位毫秒内的序列号。最前面的12位永远用不到。

优点:

这样的好处是:

  • 可以和64版本的雪花算法一样,使用69年之久。
  • 最大值562949953421311,最长15位,传给前端时不用考虑类型转换问题。
  • 每毫秒内并发最大2^5=256,即每秒并发256000,足矣。

image.png文章来源地址https://www.toymoban.com/news/detail-489725.html

代码

package com.bangcle.amp.bcl.base.utils;

/**
 * <p>TODO</p>
 *
 * @author cheng.jin.peng
 * @version V1.0.0
 * @date 2023/5/11 11:17
 */

public class SnowflakeIdWorker {
    /**
     * 开始时间截 (本次时间戳为:Thu Nov 04 2010 09:42:54 GMT+0800 (中国标准时间)----1288834974657L---1656543015264587776--19 )
     */
    private final long startTime = 1683803335498L;

    /** 机器id所占的位数 */
    private final long workerIdBits = 3L;

    /** 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);

    /** 序列在id中占的位数 */
    private final long sequenceBits = 5L;

    /** 机器ID向左移12位 */
    private final long workerIdShift = sequenceBits;

    /** 时间截向左移22位(10+12) */
    private final long timestampLeftShift = sequenceBits + workerIdBits;

    /** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) */
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);

    /** 工作机器ID(0~1024) */
    private long workerId;

    /** 毫秒内序列(0~4095) */
    private long sequence = 0L;

    /** 上次生成ID的时间截 */
    private long lastTimestamp = -1L;

    //==============================Constructors=====================================
    /**
     * 构造函数
     * @param workerId 工作ID (0~1024)
     */
    public SnowflakeIdWorker(long workerId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("workerId can't be greater than %d or less than 0", maxWorkerId));
        }
        this.workerId = workerId;
    }

    // ==============================Methods==========================================
    /**
     * 获得下一个ID (该方法是线程安全的)
     * @return SnowflakeId
     */
    public synchronized long nextId() {
        long timestamp = timeGen();

        //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(
                    String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }

        //如果是同一时间生成的,则进行毫秒内序列
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            //毫秒内序列溢出
            if (sequence == 0) {
                //阻塞到下一个毫秒,获得新的时间戳
                timestamp = tilNextMillis(lastTimestamp);
            }
        }
        //时间戳改变,毫秒内序列重置
        else {
            sequence = 0L;
        }

        //上次生成ID的时间截
        lastTimestamp = timestamp;

        //移位并通过或运算拼到一起组成64位的ID
        return ((timestamp - startTime) << timestampLeftShift)
                | (workerId << workerIdShift)
                | sequence;
    }

    /**
     * 阻塞到下一个毫秒,直到获得新的时间戳
     * @param lastTimestamp 上次生成ID的时间截
     * @return 当前时间戳
     */
    protected long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    /**
     * 返回以毫秒为单位的当前时间
     * @return 当前时间(毫秒)
     */
    protected long timeGen() {
        return System.currentTimeMillis();
    }

    /**
     * 测试
     */
    public static void main(String[] args) {
        System.out.println("开始:"+System.currentTimeMillis());

        SnowflakeIdWorker idWorker = new SnowflakeIdWorker(1);

        for (int i = 0; i < 50; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            long id = idWorker.nextId();
            System.out.println(id+"长度="+String.valueOf(id).length());

        }

        System.out.println("结束:"+System.currentTimeMillis());
    }
}

到了这里,关于雪花算法&改造16位或者15位的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • spring boot中使用雪花算法生成雪花ID

    目录 1、什么是雪花算法 2、雪花算法的优缺点 3、spring boot项目中使用雪花算法使用 雪花算法(Snowflake)是一种生成全局唯一ID的算法,由Twitter公司开发。它可以在分布式系统中生成全局唯一的ID,解决分布式系统中的数据合并和分片等问题。 雪花算法生成的ID是一个64位的长

    2024年02月02日
    浏览(32)
  • 分布式ID生成算法——雪花算法

    一、分布式ID ID可以唯一标识一条记录。 对于单体架构,我们可以使用自增ID来保证ID的唯一性。但是,在分布式系统中,简单的使用自增ID就会导致ID冲突。这也就引出了 分布式ID 问题。分布式ID也要求满足分布式系统的 高性能、高可用、高并发 的特点。 二、雪花算法 世界

    2024年02月06日
    浏览(50)
  • 分布式ID生成算法:雪花算法

    雪花算法(Snowflake)是一种分布式ID生成算法,可以生成唯一的、有序的、不重复的ID号,广泛应用于分布式系统中。其生成的ID号由64位二进制数组成,可以转换成16进制或10进制的字符串表示。 雪花算法的核心思想是将一个64位的二进制数分成四部分,分别表示时间戳、数据

    2024年02月15日
    浏览(40)
  • 雪花算法

    雪花算法( SnowFlake ),是Twitter开源 的 分布式ID生成算法  主要是由 64bit 的 long 型生成的全局 ID, 其中分为四个部分: ① 最高1位固定值 0 ,因为生成的 id     是 正整数 ② 后面的41位存储毫秒级 时间戳 ③ 第三部分的10位 存储机器码 ,包括      5位 datacenterId 和5位 workerI

    2024年02月01日
    浏览(31)
  • 拆解雪花算法生成规则

    雪花算法(Snowflake)是一种生成分布式全局唯一ID的算法,生成的ID称为Snowflake IDs或snowflakes。这种算法由Twitter创建,并用于推文的ID。目前仓储平台生成ID是用的雪花算法修改后的版本。 雪花算法几个特性 生成的ID分布式唯一和按照时间递增有序,毫秒数在高位,自增序列在

    2024年02月16日
    浏览(40)
  • 雪花算法的使用(java)

    雪花算法( Snowflake )是一种分布式唯一 ID 生成算法,能够生成唯一的、有序的、高可用的 ID,常用于分布式系统中作为全局唯一标识符(GUID)。雪花算法生成的 ID 是一个 64 位的整数,其中高位是时间戳,中间位是机器 ID,低位是序列号。 雪花算法生成的 ID 包含以下信息

    2024年02月01日
    浏览(66)
  • 细说雪花算法

    需要选择合适的方案去应对数据规模的增长,以应对逐渐增长的访问压力和数据量。 数据库的扩展方式主要包括:业务分库、主从复制,数据库分表。 雪花算法:Twitter的分布式自增ID算法,Snowflake(雪花算法是由Twitter公布的分布式主键生成算法,它能够保证不同表的主键的

    2024年02月07日
    浏览(32)
  • route命令行查看或者修改默认路由

    route print命令 打开CMD,输入route print,就可以查看我们计算机的路由表,如图 看第一项0.0.0.0的网关为192.168.1.1,说明我们计算机的网关为192.168.1.1,如果我们删除自己的网关,计算机还能正常运行吗?如图 我们来看目前的网络连接状态: 可以看到目前是:无网络访问权限,也

    2024年02月08日
    浏览(50)
  • UUID的弊端以及雪花算法

    前言 问题 一般通用解决方案 雪花算法 系统唯一ID是我们在设计一个系统的时候常常会遇见的问题,也常常为这个问题而纠结。 这篇文章就是给各位看官提供一个生成分布式唯一全局id生成方案的思路,希望能帮助到大家。 不足之处,请多多指教!! 基于 Spring Boot + MyBatis

    2024年02月07日
    浏览(82)
  • 雪花算法生成唯一数字id

    2024年02月02日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包