首页常见问题正文

集群高并发环境下如何保证分布式唯一全局ID生成?

更新时间:2023-07-04 来源:黑马程序员 浏览量:

IT培训班

  在集群高并发环境下保证分布式唯一全局ID生成是一个具有挑战性的问题。下面笔者将为大家提供几种常见的解决方案:

  1.UUID(Universally Unique Identifier)

  UUID是一个128位的全局唯一标识符,它可以在不同的计算机和时间上生成。UUID的生成是基于MAC地址、时间戳等信息,因此可以保证在分布式环境下的唯一性。您可以使用UUID库或函数来生成唯一ID。

  2.基于ZooKeeper的序列节点

  ZooKeeper是一个分布式协调服务,可以用于生成分布式唯一序列节点。每个节点在ZooKeeper上创建一个临时有序节点,节点的名称就可以作为唯一ID。这种方法需要维护ZooKeeper的稳定性和性能,并且可能会对ZooKeeper集群施加一定的压力。

  3.数据库自增主键

  在分布式环境中,可以使用数据库的自增主键来生成唯一ID。每个节点将ID的生成请求发送到中央数据库,数据库逐个分配唯一的ID,并将其返回给节点。这种方法依赖于数据库的性能和可用性,可能会成为性能瓶颈。

  4.雪花算法(Snowflake)

  雪花算法是Twitter开源的一种分布式ID生成算法。它使用了一个64位的整数,将整数的各个位段分配给不同的组成部分,包括时间戳、机器ID和序列号。通过合理配置这些部分,可以在分布式系统中生成唯一ID。雪花算法需要对机器ID进行管理,确保每个节点有唯一的ID。

  接下来我们看一个简单的代码示例,展示了如何使用Java语言实现雪花算法生成全局唯一ID:

public class SnowflakeIdGenerator {
    private final long epoch = 1625097600000L; // 自定义起始时间戳,例如2021-07-01 00:00:00的时间戳

    private final long workerIdBits = 5L;
    private final long datacenterIdBits = 5L;
    private final long sequenceBits = 12L;

    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);

    private final long workerIdShift = sequenceBits;
    private final long datacenterIdShift = sequenceBits + workerIdBits;
    private final long timestampShift = 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("Worker ID can't be greater than " + maxWorkerId + " or less than 0");
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException("Datacenter ID can't be greater than " + maxDatacenterId + " or less than 0");
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }

    public synchronized long generateId() {
        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) << timestampShift) |
                (datacenterId << datacenterIdShift) |
                (workerId << workerIdShift) |
                sequence;
    }

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

  使用示例:

public class Main {
    public static void main(String[] args) {
        SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1, 1);

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

  上述代码中,SnowflakeIdGenerator类实现了雪花算法的逻辑,使用时间戳、工作节点ID和序列号来生成全局唯一ID。每个节点需要提供一个唯一的workerId和datacenterId来保证ID的唯一性。在高并发环境下,使用synchronized关键字确保线程安全,避免生成重复的ID。

  无论我们选择哪种方案,都需要根据具体的业务需求和系统架构进行权衡和实现。同时,为了保证生成的ID的唯一性和高效性,建议对ID生成的算法和相关组件进行充分的测试和评估。

分享到:
在线咨询 我要报名
和我们在线交谈!