• 作者:老汪软件技巧
  • 发表时间:2024-09-12 21:01
  • 浏览量:

在 Spring Boot 中使用雪花算法(Snowflake)来生成分布式唯一 ID 是一种常见的实践。雪花算法由 Twitter 提出,由于其高效性和分布式环境中的唯一性,广泛应用于各种场景中。

雪花算法

雪花算法生成的 64 位长整型 ID 一般由以下几部分组成:

时间戳:通常占用 41 位,用来存储毫秒级的时间戳。数据中心 ID:通常占用若干位,用来标识数据中心。机器 ID:通常占用若干位,用来标识同一数据中心内的不同机器。序列号:通常占用 12 位,用来记录同一毫秒内产生的不同 ID。实现步骤

引入依赖

在你的 pom.xml 文件中添加必要的依赖,例如常用的依赖没有特别指定,可以使用 Spring Boot 的核心依赖。

编写 Snowflake 算法类

你可以自己实现一个简单版本的雪花算法,也可以使用已有的库。以下是一个简单的 Java 实现示例:

public class SnowflakeIdGenerator {
    private final long twepoch = 1288834974657L;
    private final long workerIdBits = 5L;
    private final long datacenterIdBits = 5L;
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    private final long sequenceBits = 12L;
    private final long workerIdShift = sequenceBits;
    private final long datacenterIdShift = sequenceBits + workerIdBits;
    private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);
    private long workerId;
    private long datacenterId;
    private long sequence = 0L;
    private long lastTimestamp = -1L;
    public SnowflakeIdGenerator(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;
    }
    public synchronized long nextId() {
        long timestamp = timeGen();
        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;
        }
        lastTimestamp = timestamp;
        return ((timestamp - twepoch) << timestampLeftShift) |
                (datacenterId << datacenterIdShift) |
                (workerId << workerIdShift) |
                sequence;
    }
    protected long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }
    protected long timeGen() {
        return System.currentTimeMillis();
    }
}

使用 Snowflake 算法

创建一个 Spring Boot 服务,并使用该服务生成唯一 ID:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class IdController {
    private final SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1, 1);
    @GetMapping("/generate-id")
    public long generateId() {
        return idGenerator.nextId();
    }
}

测试

三种常见的雪花模型模式__雪花模型案例

运行你的 Spring Boot 应用程序,访问 /generate-id 端点,即可获取到一个基于雪花算法生成的唯一 ID。

nextId方法解析详细解释

同步关键字(synchronized)

获取当前时间戳

时钟回拨校验

同一毫秒内的序列号处理

更新最后时间戳

生成唯一 ID

小结

nextId 方法利用时间戳、数据中心 ID、机器 ID 和序列号来生成全局唯一的 ID,通过位移操作将这些元素组合成一个长整型数。该方法确保即使在高并发环境中,生成的 ID 也具有唯一性和顺序性。在实际应用中,这一方法能高效支持分布式系统中的唯一标识需求。

思考题:雪花模型一毫秒内最多生成多少个id

由于序列号占用 12 位,这表示在同一个节点上,同一毫秒可以生成的 ID 数量最多为 (2^{12} = 4096) 个。

因此,如果你的系统有多台机器,每台机器都能在每毫秒生成最多 4096 个唯一 ID,那么总体上你可以通过增加机器数量来扩大每毫秒可以生成的 ID 总量。不过,单台机器在单毫秒内直接生成的 ID 数量上限就是 4096 个。