当前位置: 首页 > news >正文

雪花算法

雪花算法(Snowflake)

雪花算法是一种由Twitter开源的分布式ID生成算法,广泛应用于分布式系统中,用于生成全局唯一的ID。这些ID不仅具有唯一性,还按照时间顺序递增,便于排序和查询。以下是雪花算法的详细解析:

一、算法原理

雪花算法生成的ID是一个64位的二进制数,通常表示为long型整数。这64位被划分为以下几个部分:

  1. 符号位(1位)

    • 固定为0,表示生成的ID是正数。
  2. 时间戳(41位)

    • 记录ID生成的时间,精确到毫秒。
    • 可以表示大约69年(从自定义的纪元时间开始计算,例如2015年1月1日)。
    • 提供排序功能,根据时间戳可以对数据进行排序。
  3. 数据中心ID(5位)

    • 标识不同的数据中心。
    • 可以支持最多32个数据中心(25=32)。
  4. 机器ID(5位)

    • 标识同一数据中心内的不同机器。
    • 可以支持每个数据中心最多32台机器(25=32)。
    • 数据中心ID和机器ID组合起来,可以支持最多1024个节点(32×32=1024)。
  5. 序列号(12位)

    • 在同一毫秒内生成不同的ID。
    • 支持每个节点每毫秒生成4096个唯一的ID(212=4096)。

二、ID生成过程

  1. 获取当前时间戳

    • 记录当前时间的毫秒值,与自定义的纪元时间相减,得到时间戳差值。
  2. 获取数据中心ID和机器ID

    • 每个节点在部署时配置唯一的数据中心ID和机器ID。
  3. 生成序列号

    • 如果是同一毫秒内首次生成ID,序列号从0开始。
    • 同一毫秒内每生成一个ID,序列号递增1。
    • 如果序列号达到最大值(4095),则等待下一毫秒,序列号重置为0。
  4. 组合生成ID

    • 将时间戳差值、数据中心ID、机器ID和序列号拼接起来,形成一个64位的ID。

三、算法特点

  1. 全局唯一性

    • 通过时间戳、数据中心ID、机器ID和序列号的组合,确保生成的ID在全局范围内唯一。
  2. 有序递增

    • 由于ID中包含时间戳部分,因此生成的ID在时间上是有序递增的,便于排序和查询。
  3. 高并发性能

    • 每秒可以生成数百万个唯一的ID,满足高并发场景下的需求。
    • 生成ID的过程主要依赖于位运算和位移操作,效率高。
  4. 不依赖第三方系统

    • 不依赖数据库等第三方系统,以服务的方式部署,稳定性高。

四、应用场景

雪花算法广泛应用于分布式系统中的唯一ID生成,包括但不限于以下场景:

  • 订单号生成:在电商平台中,为每个订单生成唯一的订单号。
  • 分布式数据库主键:在分布式数据库中,为数据表的主键生成唯一的ID。
  • 分布式锁:在分布式系统中,为分布式锁生成唯一的标识。

五、注意事项

  1. 时钟回拨问题

    • 雪花算法依赖系统时钟生成时间戳。如果系统时钟回拨,可能会导致生成的ID重复。
    • 解决方案包括:
      • 拒绝生成ID,直到时间戳大于等于上一次生成ID的时间戳。
      • 使用备用策略,如记录事件并报警。
  2. 机器ID的唯一性

    • 在分布式环境中,需要确保每台机器的数据中心ID和机器ID唯一。
    • 如果机器ID重复,会导致生成的ID冲突。
  3. 高并发场景下的处理

    • 在高并发场景下,同一毫秒内可能生成大量ID。
    • 需要合理设置序列号位数,避免序列号溢出。

六、代码实现示例(Java)

以下是一个简化的雪花算法Java实现示例:

public class SnowflakeIdWorker {

    // 开始时间截 (自定义纪元时间,例如2015-01-01)
    private final long twepoch = 1420041600000L;

    // 机器id所占的位数
    private final long workerIdBits = 5L;

    // 数据标识id所占的位数
    private final long datacenterIdBits = 5L;

    // 支持的最大机器id
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);

    // 支持的最大数据标识id
    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);

    // 序列在id中占的位数
    private final long sequenceBits = 12L;

    // 机器ID向左移12位
    private final long workerIdShift = sequenceBits;

    // 数据标识id向左移17位(12+5)
    private final long datacenterIdShift = sequenceBits + workerIdBits;

    // 时间截向左移22位(5+5+12)
    private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

    // 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);

    // 工作机器ID(0~31)
    private long workerId;

    // 数据中心ID(0~31)
    private long datacenterId;

    // 毫秒内序列(0~4095)
    private long sequence = 0L;

    // 上次生成ID的时间截
    private long lastTimestamp = -1L;

    // 构造函数
    public SnowflakeIdWorker(long workerId, long datacenterId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }

    // 获得下一个ID
    public synchronized long nextId() {
        long timestamp = timeGen();

        // 如果当前时间小于上一次ID生成的时间戳, 说明系统时钟回退过,这个时候应当抛出异常
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }

        // 如果是同一时间生成的, 则进行毫秒内序列
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            // 毫秒内序列溢出
            if (sequence == 0) {
                // 阻塞到下一个毫秒, 获得新的时间戳
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            // 时间戳改变,毫秒内序列重置
            sequence = 0L;
        }

        // 上次生成ID的时间截
        lastTimestamp = timestamp;

        // 移位并通过或运算拼到一起组成64位的ID
        return ((timestamp - twepoch) << timestampLeftShift)
                | (datacenterId << datacenterIdShift)
                | (workerId << workerIdShift)
                | sequence;
    }

    // 返回以毫秒为单位的当前时间
    protected long timeGen() {
        return System.currentTimeMillis();
    }

    // 阻塞到下一个毫秒,直到获得新的时间戳
    protected long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }
}


七、总结

雪花算法是一种高效、可靠的分布式ID生成算法,通过组合时间戳、数据中心ID、机器ID和序列号,确保生成的ID在全局范围内唯一且有序递增。它广泛应用于分布式系统中的唯一ID生成,为系统的数据管理和查询提供了便利。然而,在使用雪花算法时,也需要注意时钟回拨、机器ID唯一性等问题,以确保算法的正确性和稳定性。

相关文章:

  • 1.python基础知识点1
  • STM32全系大阅兵(1)
  • 【Python】omegaconf 用法详解
  • 爬虫案例十二js 逆向模拟登录集思录网
  • [MERN] 使用 socket.io 实现即时通信功能
  • 【网络编程】事件选择模型
  • 数据库的安装与配置和创建数据库与表
  • PHIAF:基于GAN的数据增强和基于序列的特征融合的噬菌体-宿主相互作用预测
  • 现代互联网网络安全与操作系统安全防御概要
  • 采用内存局部性分配有什么好处?
  • 蓝队第三次
  • RocketMQ控制台显示NOT_CONSUME_YET
  • C++—list类的使用及模拟实现
  • C语言学习day25:WinAPI编程进阶02-第一个窗口应用程序开发
  • StringBuilder常用方法详解
  • 基于操作系统控制平台-深入剖析CPUGPU Tracing分析
  • 【Linux】初识线程
  • 网格图学习(附题单与做题思路)
  • 【开源界的Manus替代战:模块化设计 vs 跨平台实战 vs 全能开发,谁主沉浮?】
  • DNASimCLR:一种基于对比学习的基因序列数据分类的深度学习方法
  • 达恩当选罗马尼亚总统
  • 去年上海60岁及以上户籍老年人口占总人口的37.6%
  • 陈龙带你观察上海生物多样性,纪录片《我的城市邻居》明播出
  • AI创业者聊大模型应用趋势:可用性和用户需求是关键
  • 人民日报评论员:党政机关要带头过紧日子
  • 重庆城市轨道交通拟听证调价:公布两套票价方案,正征求意见