雪花算法的工具类(SnowFlakeUtil)

雪花算法是由 Twitter 公司开源的可在分布式系统中产生一个全局唯一 ID 的算法。最初 Twitter 把存储系统从 MySQL 迁移到 Cassandra,因为 Cassandra 没有顺序ID生成机制,所以开发了这样一套全局唯一ID生成服务。

SnowFlake 算法生成的 ID 是一个 64 位的整数,它的结构如下图所示:

  • 第一部分:1bit 符号位,由于都是生成 ID 都是正数,所以第一位统一为0;
  • 第二部分:41 bit 时间戳,单位是毫秒,41 位可以表示的数字多达 2^41 - 1,换算成年就是 69 年;
  • 第三部分:5 bit 机房 ID,代表最多支持 32 个机房;
  • 第四部分:5 bit 机器 ID,代表每个机房最多支持 32 台机器;
  • 第五部分:12 bit,记录同一时间(毫秒)内产生的不同 id,也就是说同一毫秒内可以产生4096个不同 id。

SnowFlake 生成的 ID 整体上按照时间自增排序,并且整个分布式系统不会产生 ID 碰撞(由 DataCenterID 和 WorkerID 区分),并且效率较高

在实现 SnowFlake 基本功能的基础上,增加部分拓展功能:

  • 定义开始时间戳,默认为 2020/01/01 08:00:00,如果使用默认的时间戳位数,那么该程序生成 ID 大概可以使用到 2089 年;
  • 机房 ID 、机器 ID 和 序列 ID 三个数据段长度可以自定义,通过构造函数传入。

方法介绍

  • timeGen
    • 描述:生成当前毫秒时间戳,相对于 2020年1月1日 8:00:00
    • 属性:private
    • 返回值:当前毫秒时间戳
  • tilNextMillis
    • 描述:阻塞直到下一毫秒
    • 属性:private
    • 返回值:下一毫秒时间戳
  • nextId
    • 描述:生成一个新的 ID
    • 返回值:新 ID

流程图

Step 1.在pom.xml添加坐标

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.7.20</version>
</dependency>

Step 2.通过hutool封装SnowFlakeUtil工具类

import cn.hutool.core.lang.Singleton;
/**
 * 雪花算法工具类
 */
public class SnowFlakeUtil {
    private static final long START_STMP = 1420041600000L;
    private static final long SEQUENCE_BIT = 9L;
    private static final long MACHINE_BIT = 2L;
    private static final long DATACENTER_BIT = 2L;
    private static final long MAX_SEQUENCE = 511L;
    private static final long MAX_MACHINE_NUM = 3L;
    private static final long MAX_DATACENTER_NUM = 3L;
    private static final long MACHINE_LEFT = 9L;
    private static final long DATACENTER_LEFT = 11L;
    private static final long TIMESTMP_LEFT = 13L;
    private long datacenterId;
    private long machineId;
    private long sequence = 0L;
    private long lastStmp = -1L;

    public SnowFlakeUtil(long datacenterId, long machineId) {
        if (datacenterId <= 3L && datacenterId >= 0L) {
            if (machineId <= 3L && machineId >= 0L) {
                this.datacenterId = datacenterId;
                this.machineId = machineId;
            } else {
                throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
            }
        } else {
            throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
        }
    }

    public synchronized long nextId() {
        long currStmp = this.getNewstmp();
        if (currStmp < this.lastStmp) {
            throw new RuntimeException("Clock moved backwards.  Refusing to generate id");
        } else {
            if (currStmp == this.lastStmp) {
                this.sequence = this.sequence + 1L & 511L;
                if (this.sequence == 0L) {
                    currStmp = this.getNextMill();
                }
            } else {
                this.sequence = 0L;
            }

            this.lastStmp = currStmp;
            return currStmp - 1420041600000L << 13 | this.datacenterId << 11 | this.machineId << 9 | this.sequence;
        }
    }

    private long getNextMill() {
        long mill;
        for(mill = this.getNewstmp(); mill <= this.lastStmp; mill = this.getNewstmp()) {
        }

        return mill;
    }

    private long getNewstmp() {
        return System.currentTimeMillis();
    }

    public static Long getDefaultSnowFlakeId() {
        return ((SnowFlakeUtil)Singleton.get(SnowFlakeUtil.class, new Object[]{1L, 1L})).nextId();
    }

    public static void main(String[] args) {
        for(int i = 0; i < 10; ++i) {
            System.out.println(getDefaultSnowFlakeId());
            System.out.println(getDefaultSnowFlakeId().toString().length());
        }

    }
}

Step 3.测试使用

public static void main(String[] args) {
    Long id = SnowFlakeUtil.getDefaultSnowFlakeId();
    System.out.println(id);
}

效果图:

posted @   紫晨快乐  阅读(6185)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示