返回

10秒Get 分布式全局唯一id的生成方法,不重复、易理解!

后端

分布式全局唯一 ID:解决分布式系统中的难题

在分布式系统中,生成全局唯一 ID 是一项关键且具有挑战性的任务。当多个独立数据库或服务需要相互通信时,为每个实体分配一个唯一的 ID 至关重要,以确保数据完整性、可追溯性和事务一致性。

传统 ID 生成方法的局限性

在关系型数据库中,自增主键是一个常见的用于生成 ID 的方法。然而,在分布式系统中,这种方法行不通,因为每个节点都有自己的主键生成器,这会导致 ID 冲突和数据不一致。

分布式全局唯一 ID 解决方法

为了解决这个问题,需要一种机制来跨节点生成唯一的 ID。以下是一些流行的方法:

UUID

UUID(通用唯一标识符)是一种通过将随机数与时间戳相结合来生成 ID 的标准方法。UUID 具有以下优点:

  • 唯一性: 由于 UUID 是随机生成的,因此在理论上是唯一的。
  • 无序性: UUID 是无序的,这意味着它们不能用于排序或范围查询。

Twitter Snowflake

Twitter Snowflake 算法是由 Twitter 开发的,用于生成分布式、有序且唯一的 ID。该算法基于以下组件:

  • 时间戳: ID 的最高位存储时间戳。
  • 工作进程 ID: ID 的中间位存储生成 ID 的工作进程 ID。
  • 序列号: ID 的最低位存储序列号,它是一个自增值。

Snowflake 算法的优点在于它:

  • 高性能: Snowflake 可以生成大量的唯一 ID。
  • 有序性: 生成的 ID 是按时间戳排序的,便于范围查询。
  • 可靠性: Snowflake 算法通过避免时间戳冲突和序列号重置来保证可靠性。

代码示例(Java):

import java.util.UUID;

public class SnowflakeIdGenerator {

    private static final long EPOCH = 1420070400000L;
    private static final long WORKER_ID_BITS = 5L;
    private static final long SEQUENCE_BITS = 12L;

    private static final long MAX_WORKER_ID = -1L ^ (-1L << WORKER_ID_BITS);
    private static final long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BITS);

    private final long workerId;
    private long sequence = 0L;
    private long lastTimestamp = -1L;

    public SnowflakeIdGenerator(long workerId) {
        if (workerId < 0 || workerId > MAX_WORKER_ID) {
            throw new IllegalArgumentException("Worker ID must be between 0 and " + MAX_WORKER_ID);
        }
        this.workerId = workerId;
    }

    public synchronized long nextId() {
        long timestamp = timeGen();
        if (timestamp < lastTimestamp) {
            throw new RuntimeException("Invalid system clock");
        }
        if (timestamp == lastTimestamp) {
            sequence = (sequence + 1) & MAX_SEQUENCE;
            if (sequence == 0L) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }
        lastTimestamp = timestamp;
        return timestamp << (WORKER_ID_BITS + SEQUENCE_BITS) | (workerId << SEQUENCE_BITS) | sequence;
    }

    private long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    private long timeGen() {
        return System.currentTimeMillis();
    }

    public static void main(String[] args) {
        SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1L);
        for (int i = 0; i < 10; i++) {
            System.out.println(idGenerator.nextId());
        }
    }
}

Redis

Redis 也可以用于生成分布式全局唯一 ID。它提供了一个名为 INCR 的命令,该命令会递增给定键的值,并返回递增后的值。

Redis ID 生成方法的优点是:

  • 高性能: Redis 可以快速生成大量的唯一 ID。
  • 简单性: Redis 的 INCR 命令易于使用和实现。

Redis ID 生成方法的缺点是:

  • 依赖性: 它依赖于 Redis 服务的可用性。
  • 无序性: 生成的 ID 是无序的。

代码示例(Python):

import redis

r = redis.Redis(host='localhost', port=6379, db=0)
id = r.incr('my_id_key')

总结

在分布式系统中生成全局唯一 ID 对于确保数据完整性和事务一致性至关重要。有几种方法可以解决这个问题,包括 UUID、Twitter Snowflake 和 Redis。每种方法都有其优缺点,因此根据具体需求选择最合适的方法非常重要。

常见问题解答

1. UUID 和 Snowflake 算法之间有什么区别?

UUID 是随机生成的,而 Snowflake 算法是基于时间戳和序列号的。Snowflake 算法可以生成有序且高效的 ID,而 UUID 则是无序的。

2. Redis 的 INCR 命令如何保证 ID 的唯一性?

INCR 命令使用原子操作来递增给定键的值。这意味着多个客户端同时请求 ID 时不会发生冲突。

3. 哪个 ID 生成方法最适合大型分布式系统?

对于大型分布式系统,Twitter Snowflake 算法是一个很好的选择,因为它可以生成大量有序且高效的 ID。

4. 如何处理 Snowflake 算法中的时间戳回退?

如果发生时间戳回退,Snowflake 算法会丢弃重复的 ID。这有助于确保 ID 的唯一性。

5. 我应该在何时使用 UUID?

UUID 最适合需要生成非顺序且不涉及范围查询或排序的 ID 的情况。