啥是雪花算法啊?顺便举个栗子

雪花算法(Snowflake Algorithm)是一种用于生成唯一标识符的算法,主要用于分布式系统中对数据进行唯一标识。它最初由Twitter开发,用于在分布式系统中生成全局唯一的ID。

这个算法的原理贼拉简单,它将生成的ID分成三个部分:

  1. 时间戳: 记录了雪花生成的时间,就像雪花落下的瞬间一样。这确保了即使是在同一时刻生成的雪花,它们的ID也会有所不同。 使用了41位来存储自Unix纪元(1970年1月1日)以来的毫秒数。这允许Snowflake在一定的时间内生成唯一的ID。

  2. 机器ID: 就像是雪花的身份证号,用来标识这片雪花是由哪台机器生成的。这样可以避免不同机器生成相同的ID。这个机器码占用了10位。这样可以确保在同一时间戳内由不同机器生成的ID不会重复。

  3. 序列号: 在同一时刻,可能有多个雪花同时生成,序列号就像是雪花的兄弟姐妹们的排行,确保它们在同一时刻内有序。 在同一毫秒内,可以生成多个ID。序列号占用了12位,用于标识同一机器在同一时间戳内生成的不同ID。

想象一下,每个雪花ID就像是一个小档案,里面都包含了这三个关键信息,不同时刻的由时间戳区分了,同一时刻的不同机器上的又由机器码区分了,同一台机器上的又被序列号区分了,所以这种结构保证了在分布式系统中生成的ID是唯一的,并且有一定的顺序性,可以根据时间戳进行排序。

总的来说,雪花算法就是通过这三个要素,巧妙地保证了在分布式系统中生成的ID既是全局唯一的,又有一定的时间顺序。这种设计就像是为雪花们制定了一个独特的身份系统,让它们在大雪纷飞的世界中能够有条不紊地降落。

举个例子,在Java中,可以使用雪花算法生成唯一ID的实现通常是通过类来完成的。以下是一个简单的Java示例代码,使用雪花算法生成唯一ID:

java 复制代码
package com.luke.luketools.snowflakeIdGenerator;

public class SnowflakeIdGenerator {
    // 2021-01-01 00:00:00
    private final long epoch = 1609459200000L; // 设置起始时间戳,例如2021-01-01 00:00:00的毫秒数
    // 机器ID所占的位数
    private final long machineIdBits = 10L;
    // 机器ID的最大值
    private final long maxMachineId = -1L ^ (-1L << machineIdBits);
    // 毫秒内自增位
    private final long sequenceBits = 12L;
    // 机器ID向左移12位
    private final long machineIdShift = sequenceBits;
    // 时间戳向左移22位(10+12)
    private final long timestampLeftShift = sequenceBits + machineIdBits;
    // 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);
    // 机器ID(0~1023)
    private long machineId;
    // 毫秒内序列(0~4095)
    private long sequence = 0L;
    // 上次生成ID的时间戳
    private long lastTimestamp = -1L;
    // 构造器
    public SnowflakeIdGenerator(long machineId) {
        if (machineId < 0 || machineId > maxMachineId) {
            throw new IllegalArgumentException("Machine ID must be between 0 and " + maxMachineId);
        }
        this.machineId = machineId;
    }
    // 线程安全的id生成方法
    public synchronized long generateId() {
        long timestamp = System.currentTimeMillis();

        if (timestamp < lastTimestamp) {
            // 如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过,出现问题返回-1
            throw new RuntimeException("Clock moved backwards. Refusing to generate ID for " + (lastTimestamp - timestamp) + " milliseconds.");
        }
        // 如果是同一时间生成的,则进行毫秒内序列
        if (lastTimestamp == timestamp) {
            // sequence自增,因为sequence只有12bit,所以和sequenceMask相与一下,去掉高位
            sequence = (sequence + 1) & sequenceMask;
            // 判断是否溢出,也就是每毫秒内超过4095,当为4096时,与sequenceMask相与,sequence就等于0
            if (sequence == 0) {
                // 自旋等待到下一毫秒
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            // 如果不是同一时间生成的,则重置sequence
            sequence = 0L;
        }
        // 上次生成ID的时间戳
        lastTimestamp = timestamp;
        // 移位并通过或运算拼到一起组成64位的ID
        return ((timestamp - epoch) << timestampLeftShift) |
                (machineId << machineIdShift) |
                sequence;
    }
    // 自旋等待到下一毫秒
    private long tilNextMillis(long lastTimestamp) {
        // 获取当前时间戳
        long timestamp = System.currentTimeMillis();
        // 如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过,出现问题返回-1
        while (timestamp <= lastTimestamp) {
            // 获取当前时间戳
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }

    public static void main(String[] args) {
        // 使用机器ID为1的实例
        SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1);

        // 生成10个ID并打印
        for (int i = 0; i < 10; i++) {
            long id = idGenerator.generateId();
            System.out.println("Generated ID: " + id);
        }
    }
}

输出结果:

这只是一个简单的实现示例,机器码使用了一位。在实际生产环境中,还需要考虑高并发情况下的性能和线程安全性。

相关推荐
风象南3 分钟前
SpringBoot 自研「轻量级 API 防火墙」:单机内嵌,支持在线配置
后端
刘一说14 分钟前
CentOS 系统 Java 开发测试环境搭建手册
java·linux·运维·服务器·centos
Victor35620 分钟前
Redis(14)Redis的列表(List)类型有哪些常用命令?
后端
Victor35620 分钟前
Redis(15)Redis的集合(Set)类型有哪些常用命令?
后端
卷福同学21 分钟前
来上海三个月,我在马路边上遇到了阿里前同事...
java·后端
bingbingyihao2 小时前
多数据源 Demo
java·springboot
在努力的前端小白7 小时前
Spring Boot 敏感词过滤组件实现:基于DFA算法的高效敏感词检测与替换
java·数据库·spring boot·文本处理·敏感词过滤·dfa算法·组件开发
bobz9659 小时前
小语言模型是真正的未来
后端
一叶飘零_sweeeet9 小时前
从繁琐到优雅:Java Lambda 表达式全解析与实战指南
java·lambda·java8
DevYK10 小时前
企业级 Agent 开发实战(一) LangGraph 快速入门
后端·llm·agent