雪花算法的实现原理

目录

    • 一、简介
      • 1.什么是雪花算法?
      • 2.为什么使用雪花算法?
      • 3.雪花算法的缺点
    • 二、实现原理
      • 1bit-符号位(基本不用)
      • 41bit-时间戳
      • 10bit-机器位
      • 12bit-序列号
    • 三、代码实现
      • 1.hutool工具
      • 2.逆向解析
      • 3.hutool源码解析

雪花算法的实现原理_第1张图片

一、简介

1.什么是雪花算法?

雪花算法 英文名为 SnowFlake,是一个64个big位组成的 long 类型的数字,由 Twitter 开源的分布式 ID 算法生成。主要应用于分布式环境下生成全局唯一ID。

雪花算法的意思是生成的ID如雪花一般独一无二。

2.为什么使用雪花算法?

如果我们单单只想解决全局唯一ID这个问题有很多的解决方法:比如 UUID系统时间戳Redis原子递增数据库全局表自增ID等。但在实际应用中,我们需要的ID除了唯一性之外,还需要满足以下特征:

1)单调递增:保证下一个ID号一定大于上一个。

2)保证安全:ID号需要无规则性,不能让别人根据ID号猜出我们的信息和业务数据量,增加恶意用户扒取数据的难度。

3)含时间戳:ID需要记录系统时间戳。

4)高可用:获取分布式ID的请求,服务器至少要保证 99.999% 的情况下给创建一个全局唯一的分布式ID。

5)低延迟:获取分布式ID的请求,要快,急速。

6)高QPS:服务器要顶得住并且成功创建10w个分布式ID。

雪花算法就是一个比较符合这类特征的全局唯一算法。很多大厂的全局ID组件中,都有用到,比如百度的 UidGenerator、美团的 Leaf 算法等。

3.雪花算法的缺点

由于雪花算法严重依赖时间,所以当发生服务器时钟回拨时可能会产生重复ID。

二、实现原理

雪花算法主要由4个部分组成:

雪花算法的实现原理_第2张图片

1bit-符号位(基本不用)

1位标识:由于 long 基本类型在 Java 中是带符号的,最高位是符号位,正数是0,负数是1。由于 id 一般是正数,所以第一位都是0。

41bit-时间戳

接下来41位存储毫秒级时间戳,41位可以表示 2^41-1 毫秒。

转化成年则是:(2^41-1)/(1000*60*60*24*356)=69 年。也就是说这个时间戳大概可以使用 69年 不重复。

注意: 这里的41位时间戳不是存储当前时间的时间戳,而是存储时间戳的差值 “当前时间戳 - 开始时间戳” 得到的值。这里的开始时间戳,一般是我们的 id 生成器开始使用的时间,由程序指定,设置好后就不要去变更了,切记!!!由此,雪花算法有了服务器时间回拨可能会生成重复id的缺点。

10bit-机器位

10位的数据机器位,包括 5 位 datacenterId 和 5 位 workerId,最多可以部署 2^10=1024 台机器。

这里的 5 位可以表示的最大整数时 2^5-1=31,即可以用 0、1、2、3、…31 这 32 个数字,来表示不同的 datacenterId 或 workerId

12bit-序列号

用来记录同毫秒内产生的不同ID,12位的计数顺序支持每个节点每毫秒(同一机器,同一时间戳)产生 4096 个ID序号。

理论上雪花算法方案的 QPS 约为 409.6w/s,这种分配方式可以保证在任何一个 IDC 的任何一台机器在任意毫秒内生成的ID都是不同的。

三、代码实现

1.hutool工具

关于具体实现网上有很多,这里就不赘述了,需要的话可以参考一下 hutool 中雪花算法的生成,依赖如下:

<dependency>
    <groupId>cn.hutoolgroupId>
    <artifactId>hutool-allartifactId>
    <version>5.8.16version>
dependency>

使用示例如下:

import cn.hutool.core.util.IdUtil;

public static void main(String[] args) {
    System.out.println(IdUtil.getSnowflakeNextId());
}

网上很多实现方法比较简单,针对机器数部分的处理不太够,在分布式场景下并发容易导致id重复。hutool 中的雪花算法是通过获取 MAC 地址来进行生成的,经过了大量的验证,还是比较严谨的,所以不推荐自己实现雪花算法。

2.逆向解析

了解了雪花算法的实现原理(1位符号位 + 41位时间戳 + 5位数据中心ID + 5位机器ID + 12位序列号)之后,我们就可以根据已有的雪花算法ID来逆向解析出相应的信息。

解析代码实现如下:

AnalyzeSnowflake.java

import cn.hutool.core.util.IdUtil;

import java.text.SimpleDateFormat;
import java.util.Date;

public class AnalyzeSnowflake {

    public static void main(String[] args) {
        // 生成雪花算法ID
        // timestamp - this.twepoch << 22 | this.dataCenterId << 17 | this.workerId << 12 | this.sequence
        long snowflakeNextId = IdUtil.getSnowflakeNextId();
        System.out.println("snowflake:" + snowflakeNextId);

        // 补零
        StringBuilder binaryString = new StringBuilder(Long.toBinaryString(1686769506909614080L)); // 将long类型的数值转换为二进制字符串
        int addZeroCount = 64 - binaryString.length();
        for (int i = 0; i < addZeroCount; i++) {
            binaryString.insert(0, "0");
        }
        System.out.println("length:" + binaryString.length());

        // 1位符号位 + 41位时间戳 + 5位数据中心ID + 5位机器ID + 12位序列号
        System.out.println("part1-符号位:" + binaryString.charAt(0));
        long millis = Long.parseLong(binaryString.substring(1, 42), 2);
        long second = millis / 1000;
        long minute = second / 60;
        long hour = minute / 60;
        long day = hour / 24;
        long year = day / 365;
        day = day % 365;
        hour = hour % 24;
        minute = minute % 60;
        second = second % 60;
        System.out.println("part2-从开始到现在运行了:" + String.format("%d年%d天%d小时%d分钟%d秒", year, day, hour, minute, second));
        long startMillis = System.currentTimeMillis() - millis;
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("part2-开始时间大约在:" + simpleDateFormat.format(new Date(startMillis)));
        System.out.println("part3-1-数据中心id:" + Long.parseLong(binaryString.substring(43, 47), 2));
        System.out.println("part3-2-机器id:" + Long.parseLong(binaryString.substring(48, 52), 2));
        System.out.println("part4-序列号:" + Long.parseLong(binaryString.substring(53, 64), 2));
    }
}

执行结果:

雪花算法的实现原理_第3张图片

3.hutool源码解析

在 hutool 的 Snowflake.java 源码中我们可以看到起始时间默认为:2010-11-04 01:42:54,与我们推算的时间基本一致。

雪花算法的实现原理_第4张图片

往下拉,在 Snowflake.java 可以看到雪花算法的核心实现:

timestamp - this.twepoch << 22 | this.dataCenterId << 17 | this.workerId << 12 | this.sequence

雪花算法的实现原理_第5张图片

整理完毕,完结撒花~





参考地址:

1.雪花算法详解(原理优缺点及代码实现),https://www.cnblogs.com/mikechenshare/p/16787023.html

2.雪花算法原理及实现,https://blog.csdn.net/qq_41573860/article/details/124119358

3.通俗聊透雪花算法的实现原理,https://baijiahao.baidu.com/s?id=1750456904521809034&wfr=spider&for=pc

你可能感兴趣的:(Java,算法)