雪花算法的实现原理

在这里插入图片描述

一、简介

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个部分组成:

在这里插入图片描述

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.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.16</version>
</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.hutool源码解析

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

在这里插入图片描述

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

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

在这里插入图片描述

整理完毕,完结撒花~ 🌻





参考地址:

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不愿放下技术的小赵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值