雪花算法介绍

概述

Snowflake算法是Twitter开发的一种分布式唯一ID生成算法,用于解决在分布式系统中生成全局唯一ID的问题。该算法的核心思想是通过对64位的ID进行合理的位分配,保证在分布式环境下生成的ID具有全局唯一性和有序性。

Snowflake算法结构

在这里插入图片描述 64位ID结构:Snowflake算法生成的ID是一个64位的整数,其中各部分如下所示: 符号位(第1位):始终为0,用于保证生成的ID为正数。时间戳(41位):记录生成ID的时间戳,单位是毫秒,可以支持约69年的时间范围。机器ID(10位):标识机器的唯一ID,每台机器需要分配一个唯一的ID。序列号(12位):表示同一毫秒内生成的序列号,支持每台机器每毫秒生成4096个ID。
最高位是符号位,始终为0,不可用。
41位的时间序列,精确到毫秒级,41位的长度可以使用69年。时间位还有一个很重要的作用是可以根据时间进行排序。注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截) 后得到的值,这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序SnowFlake类的START_STMP属性)。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69
10位的机器标识,10位的长度最多支持部署1024个节点。
12位的计数序列号,序列号即一系列的自增id,可以支持同一节点同一毫秒生成多个ID序号,12位的计数序列号支持每个节点每毫秒产生4096个ID序号。
加起来刚好64位,为一个Long型。这个算法很简洁,但依旧是一个很好的ID生成策略。其中,10位器标识符一般是5位IDC+5位machine编号,唯一确定一台机器。

雪花算法一种生成分布式全局唯一 ID 算法,生成 ID 称为SnowflakeIDs。这种算法由 Twitter 创,并用于推文 ID。一个 Snowflake ID 有 64 位元。
第 1 位:Java 中 long 最高位符号位代表正负,正数 0,负数1
一般生成 ID 都为正数,所以默认为 0。下来前 41 位时间戳,表示了自选定时期以来毫秒数。下来 10 位代表计算机 ID,防止冲突。
其余 12 位代表每台机器上生成 ID 序列号,这允许在同一毫秒内创多个Snowflake ID。
雪花算法一般用来实现全局唯一的业务主键,解决分库分表之后主键id的唯一性问题。
而雪花算法就是一个比较符合这类特征的全局唯一算法,在美团的Leaf组件中也有用到。
它是由一个64位的long类型数字组成,分为四个部分。
第一部分,用1个bit表示符号位,一般情况下是0
第二部分,用41个bit来表示时间戳,使用系统时间的毫秒数
第三部分,用10个bit来记录工作机器id,这样就可以保证在多个服务器上生成的id的唯一性。
如果存在跨机房部署,我们还可以把它分成两个5bit,前面5个bit可以表示机房id,后面5个bit可以
表示机器id。
第四个部分,用12个bit表示序列号,表示一个递增序列,用来记录同毫秒内产生的不同id
雪花算法,就是根据这四个部分的规则,生成对应的bit位数据,然后组装在一起形成一个全局唯一id。
雪花算法英文翻译为 Snow Flake 算法,它是由Twitter开源的分布式 ID生成算法。主要应用于分库分表场
景中的全局ID作为业务主键,或者生成全局唯一的订单号。
那为什么要叫雪花算法呢?据相关研究表示,一般的雪花大约由10的19次方个水分子组成。在雪花形成过
程中,会形成不同的结构分支,所以说大自然中不存在两片完全一样的雪花,每一片雪花都拥有自己独特的形
状。雪花算法的意思是表示生成的ID如雪花一般独一无二。
实现原理
雪花算法那是一个由64个Bit(比特)位组成的long类型的数字。如图所示,它分为四个部分。
第一部分,用1个bit(比特)位来表示1个符号位,因为ID一般不会是负数,所以一般情况下就是0。
第二部分,用41个bit(比特)位来表示系统时间戳,这个时间戳就是系统时间记录的毫秒数。41个bit可以表示的
最大数字为2的41次方减1毫秒,换算成时间为69年。
第三部分,用10个bit(比特)位来技术工作机器的ID,用来保证多个服务器上生成ID的唯一性。如果存在跨机
房部署的情况,还可以把这10个比特位拆分为两组,每组5个bit(比特)位。前面的5个bit(比特)表示机房ID,
后面5个bit(比特)表示机器ID。10个比特位最大值是2的10次方,也就是最多1024台机器。
第四部分,用12个比特位来表示递增序列,用来记录同一毫秒内产生不同ID的能力。它的最大值为2的12次方减1,也就是4096。
雪花算法就是根据这四个部分的组成规则,生成对应Bit位的数据,然后组装到一起生成一个全局唯一ID。

雪花算法的优缺点

雪花算法主要有以下优点:
1)分布式系统内不会产生ID碰撞,效率高。
2)不需要依赖数据库等第三方系统,稳定性更高,可以根据自身业务分配bit位,非常灵活。
3)生成ID的性能也非常高,每秒能生成26万个自增可排序的ID。
优点:实现简单、高效,生成ID速度快,ID呈递增趋势,适合在分布式系统中大规模使用。
缺点:依赖系统时钟,如果系统时间回拨可能会导致ID重复;机器ID需要提前分配,限制了扩展性。
当然,雪花算法那也有缺点,因为它依赖机器时钟,如果机器时钟回拨,可能会导致ID重复。如果再分布式环境下,每台机器上的时钟不可能完全同步,有时候会出现不是全局递增的情况。当然,大部分情况下可以忽略这一缺点。

雪花算法时钟回拨问题

雪花算法(Snowflake)是一种用于生成分布式系统中全局唯一的ID的算法,最初由Twitter开发。它的核心思想是利用时间戳、机器ID和序列号来保证生成的ID在分布式环境下的唯一性。
时钟回拨对于雪花算法来说是一个潜在的问题,因为雪花算法中的唯一ID生成依赖于时间戳。如果系统时钟发生回拨,那么可能会导致生成的ID出现重复或不符合预期的情况。
针对时钟回拨问题,通常可以采取以下策略来解决:

  1. 时钟同步: 确保系统中各个节点的时钟保持同步,减小时钟回拨的概率。这可以通过使用NTP(Network Time Protocol)等时钟同步工具来实现。
  2. 时钟检测和补偿: 在雪花算法的实现中,可以引入时钟回拨的检测机制,一旦检测到时钟回拨,可以暂停ID生成,等待时钟同步后再继续生成ID,或者采用其他策略进行补偿,确保生成的ID不重复。
  3. 容错处理: 针对时钟回拨带来的异常情况,可以引入一些容错机制,例如记录时钟回拨的发生以及相应的处理方式,以便在遇到类似情况时能够正确处理并恢复正常状态。
    总的来说,针对雪花算法中的时钟回拨问题,需要在实现时考虑时钟同步、时钟检测和补偿、容错处理等策略,以确保生成的ID在分布式环境下仍然能够保持唯一性和正确性。

解决唯一ID这个问题解决方法

比如UUID、系统时间戳、Redis原子递增、数据库全局表
自增ID等等。但是在实际应用中,我们需要的ID除了唯一性以外,还需要满足以下特征:
1)、单调递增:保证下一个ID号一定大于上一个。
2)、保证安全:ID号需要无规则性,不能让别人根据ID号猜出我们的信息和业务数据量,增加恶意用户扒取数据的难度。
3)、含时间戳:需要记录系统时间戳
4)、高可用:发布一个获取分布式ID的请求,服务器至少要保证99.999%的情况下给创建一个全局唯一的分布式ID。
5)、低延迟:发布一个获取分布式ID的请求,要快,急速。
6)、高QPS:假如并发一口气10万个创建分布式ID请求同时杀过来,服务器要顶得住并且成功创建10万个分布式ID。
雪花算法就是一个比较符合这类特征的全局唯一算法。在很多大厂的全局ID组件中,都有用到,比如百度的UidGenerator、美团的Leaf算法等等。

Snowflake算法生成ID的过程

生成ID的过程:
当需要生成一个新的ID时,Snowflake算法根据当前时间戳、机器ID和序列号来生成一个唯一的ID。
首先获取当前时间戳,然后将时间戳左移22位,给机器ID留出10位的空间。
将机器ID左移12位,空出序列号的位数。
序列号部分初始化为0,如果同一毫秒内生成多个ID,序列号递增,超过4096则等待下一毫秒再生成。
ID的唯一性:
Snowflake算法通过合理分配时间戳、机器ID和序列号来保证生成的ID在分布式环境中的唯一性。不同机器的ID不同,同一毫秒内生成的ID序列号也不同,这样可以避免ID重复的情况发生。
总的来说,Snowflake算法是一种简单有效的分布式唯一ID生成算法,适用于各种分布式系统场景,但在使用时需要注意时钟同步和机器ID的分配等问题。

Java 实现示例

以下是一个简单的 Java 实现示例,用于生成 Snowflake 算法的唯一 ID:

public class SnowflakeIdGenerator {

    private final long workerId;
    private final long epoch = 1609459200000L; // 起始时间戳,这里以2021-01-01 00:00:00为起始时间
    private long sequence = 0L;
    private final long workerIdBits = 5L;
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
    private final long timestampLeftShift = 22L;
    private final long workerIdShift = 12L;
    private final long sequenceMask = -1L ^ (-1L << 12L);
    private long lastTimestamp = -1L;

    public SnowflakeIdGenerator(long workerId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException("Worker ID can't be greater than " + maxWorkerId + " or less than 0");
        }
        this.workerId = workerId;
    }

    public synchronized long nextId() {
        long timestamp = System.currentTimeMillis();
        if (timestamp < lastTimestamp) {
            throw new RuntimeException("Clock moved backwards. Refusing to generate id for " + (lastTimestamp - timestamp) + " milliseconds");
        }
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }
        lastTimestamp = timestamp;
        return ((timestamp - epoch) << timestampLeftShift) | (workerId << workerIdShift) | sequence;
    }

    private long tilNextMillis(long lastTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }

    public static void main(String[] args) {
        SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1); // 传入当前机器的ID
        System.out.println(idGenerator.nextId());
    }
}

在上面的示例中,我们创建了一个名为 SnowflakeIdGenerator 的类,通过调用 nextId() 方法来生成唯一的 ID。在 main 方法中,我们实例化了 SnowflakeIdGenerator 并调用 nextId() 方法来生成一个 ID。请注意,这里的 workerId 需要根据实际情况设置为当前机器的唯一标识符。
总之,这段代码演示了如何使用 Java 实现基本的 Snowflake 算法来生成唯一的 ID。

  • 20
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

思静语

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

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

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

打赏作者

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

抵扣说明:

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

余额充值