雪花算法介绍

本文详细介绍了Snowflake算法的结构、优缺点,探讨了时钟回拨问题及其解决方案,并提供了Java实现示例。Snowflake被广泛用于分布式系统中生成全局唯一ID,确保业务主键的唯一性。

概述

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。

雪花算法(Snowflake Algorithm)是 Twitter 开源的一种分布式唯一 ID 生成算法,其核心思想是通过组合时间戳、机器 ID 和序列号来生成全局唯一的 64 位长整型 ID[^3]。该算法使用一个 64 位的整数作为 ID,并将这个整数划分为不同的部分,每个部分表示不同的含义,整体结构为:0 | 0000000000 0000000000 0000000000 0000000000 | 00000 | 00000 | 000000000000 [^1]。 雪花算法生成的 ID 具有全局唯一性、有序性和高性能等特点。通过时间戳、机器 ID 和序列号的组合,能确保生成的 ID 在分布式系统中唯一;ID 中包含时间戳信息,因此生成的 ID 在时间上是有序的,方便排序和查询;生成 ID 的过程基于位运算,效率高,适合高并发场景。在高并发分布式环境下,每秒可生成百万个不重复 ID,且基本保证 ID 有序递增,不依赖第三方库或者中间件,算法简单,在内存中进行,效率高[^3][^5]。 不过,雪花算法也存在一定缺点。它依赖系统时钟,如果系统时间回拨可能会导致 ID 重复;机器 ID 需要提前分配,限制了扩展性。在分布式环境下,每台机器上的时钟不可能完全同步,有时候会出现不是全局递增的情况,但大部分情况下可以忽略这一缺点[^4]。 以下是一个简单的 Python 实现雪花算法的示例代码: ```python import time # 起始时间戳(2020-01-01 00:00:00) START_TIMESTAMP = 1577836800000 # 数据中心 ID 所占位数 DATACENTER_ID_BITS = 5 # 机器 ID 所占位数 WORKER_ID_BITS = 5 # 序列号所占位数 SEQUENCE_BITS = 12 # 数据中心 ID 最大值 MAX_DATACENTER_ID = -1 ^ (-1 << DATACENTER_ID_BITS) # 机器 ID 最大值 MAX_WORKER_ID = -1 ^ (-1 << WORKER_ID_BITS) # 序列号最大值 MAX_SEQUENCE = -1 ^ (-1 << SEQUENCE_BITS) # 机器 ID 向左移位数 WORKER_ID_SHIFT = SEQUENCE_BITS # 数据中心 ID 向左移位数 DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS # 时间戳向左移位数 TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATACENTER_ID_BITS class SnowflakeIDGenerator: def __init__(self, datacenter_id, worker_id): if datacenter_id > MAX_DATACENTER_ID or datacenter_id < 0: raise ValueError(f"Datacenter ID must be between 0 and {MAX_DATACENTER_ID}") if worker_id > MAX_WORKER_ID or worker_id < 0: raise ValueError(f"Worker ID must be between 0 and {MAX_WORKER_ID}") self.datacenter_id = datacenter_id self.worker_id = worker_id self.sequence = 0 self.last_timestamp = -1 def _get_current_timestamp(self): return int(time.time() * 1000) def _wait_for_next_millis(self, last_timestamp): timestamp = self._get_current_timestamp() while timestamp <= last_timestamp: timestamp = self._get_current_timestamp() return timestamp def get_id(self): timestamp = self._get_current_timestamp() if timestamp < self.last_timestamp: raise Exception("Clock moved backwards. Refusing to generate id for {} milliseconds".format( self.last_timestamp - timestamp)) if timestamp == self.last_timestamp: self.sequence = (self.sequence + 1) & MAX_SEQUENCE if self.sequence == 0: timestamp = self._wait_for_next_millis(self.last_timestamp) else: self.sequence = 0 self.last_timestamp = timestamp id = ((timestamp - START_TIMESTAMP) << TIMESTAMP_LEFT_SHIFT) | \ (self.datacenter_id << DATACENTER_ID_SHIFT) | \ (self.worker_id << WORKER_ID_SHIFT) | \ self.sequence return id # 使用示例 generator = SnowflakeIDGenerator(datacenter_id=1, worker_id=1) for _ in range(10): print(generator.get_id()) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

思静鱼

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

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

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

打赏作者

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

抵扣说明:

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

余额充值