分布式多个机器生成id,如何保证不重复

92 阅读4分钟

分布式系统中生成唯一 ID,必须满足 全局唯一性、高性能、可扩展性 的要求。以下是几种常见的方案:

  1. Snowflake 算法(Twitter 雪花算法)🔥

适用于 高并发分布式系统,每个 ID 是 64 位整数,格式如下:

💡 生成逻辑

  1. 当前时间戳(41 bit)

  2. • 41 bit 可表示 69 年(2^41 / (1000606024≈ 69)。

  3. 机器 ID(10 bit)

• 通常 5 bit 数据中心 + 5 bit 机器 ID,总计支持 1024 台服务器

  1. 毫秒内序列号(12 bit)

同一毫秒内最多 4096 个 ID,如果超过,则等待下一毫秒。

  1. 最终拼接成 64 位整数,确保全局唯一。

💻 代码实现(Python 版)

import time
import threading

class SnowflakeIDGenerator:
    def __init__(self, data_center_id, machine_id):
        self.epoch = 1700000000000  # 起始时间戳(毫秒)
        self.data_center_id = data_center_id
        self.machine_id = machine_id
        self.sequence = 0
        self.last_timestamp = -1
        self.lock = threading.Lock()

    def _current_timestamp(self):
        return int(time.time() * 1000)  # 获取当前毫秒时间戳

    def _wait_for_next_millis(self, last_timestamp):
        while True:
            timestamp = self._current_timestamp()
            if timestamp > last_timestamp:
                return timestamp

    def generate_id(self):
        with self.lock:
            timestamp = self._current_timestamp()

            if timestamp == self.last_timestamp:
                self.sequence = (self.sequence + 1) & 0xFFF  # 4096 内循环
                if self.sequence == 0:
                    timestamp = self._wait_for_next_millis(self.last_timestamp)
            else:
                self.sequence = 0

            self.last_timestamp = timestamp

            # 组合 ID
            return ((timestamp - self.epoch) << 22) | (self.data_center_id << 17) | (self.machine_id << 12) | self.sequence

# 示例
generator = SnowflakeIDGenerator(data_center_id=1, machine_id=1)
print(generator.generate_id())  # 生成一个唯一 ID

✅ 优势

高性能:单机每毫秒可生成 4096 个 ID。

去中心化:无数据库依赖,支持 分布式环境

按时间递增:适合排序索引,如 MySQL 的 BIGINT 主键。

❌ 缺点

• 需要 提前分配机器 ID(防止机器 ID 重复)。

• 服务器时间 回拨 会导致 ID 可能重复(可用 NTP 同步时钟)。

  1. UUID(通用唯一标识符)

💡 生成逻辑

UUID 是 128 位字符串,常见格式:

550e8400-e29b-41d4-a716-446655440000

Python 生成 UUID:

import uuid
print(uuid.uuid4())  # 生成随机 UUID

✅ 优势

去中心化,不依赖数据库或中心节点。

可离线生成,适合分布式环境。

❌ 缺点

ID 太长(128 bit) ,存储效率低。

无序性,不适合数据库索引(插入性能差)。

  1. 数据库自增 ID(MySQL / PostgreSQL)

💡 生成逻辑

CREATE TABLE id_generator (
    id BIGINT AUTO_INCREMENT PRIMARY KEY
);

然后使用:

INSERT INTO id_generator VALUES ();
SELECT 0;

✅ 优势

简单易用,不需要复杂的算法。

有序递增,适合数据库索引。

❌ 缺点

数据库单点瓶颈,高并发下 ID 分配速度有限。

分布式集群难以协调,可能产生冲突。

🚀 解决方案

使用 MySQL 分布式 ID 方案

CREATE TABLE id_generator (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    shard_id INT NOT NULL
) AUTO_INCREMENT=100000;

不同机器分配不同起始 ID,如:

• 机器 1:从 100000 开始,每次递增 1000。

• 机器 2:从 100001 开始,每次递增 1000。

  1. Redis 生成全局 ID

💡 生成逻辑

使用 Redis 自增键 INCR

SET order_id 1000000
INCR order_id

Python 代码:

import redis

r = redis.Redis(host='localhost', port=6379, db=0)
order_id = r.incr("order_id")
print(order_id)  # 输出唯一 ID

✅ 优势

高并发支持,Redis 单实例 每秒可处理 10w+ ID

可集群扩展,适合多个节点。

❌ 缺点

Redis 挂掉 ID 会丢失(可以使用持久化 RDB)。

需要 Redis 服务器,有一定部署成本。

  1. 基于 Zookeeper 的全局 ID

Zookeeper 提供 顺序节点(ZNode) ,可以保证全局唯一 ID:

from kazoo.client import KazooClient

zk = KazooClient(hosts='localhost:2181')
zk.start()

node = zk.create("/unique_id", b"", sequence=True)
print(node)  # "/unique_id0000000001"

zk.stop()

✅ 优势

天然分布式支持

有序 ID 适合数据库索引

❌ 缺点

性能较低,每秒最多生成 数千个 ID

Zookeeper 需要维护,架构复杂。

🎯 方案对比

💡 最佳实践

高并发业务(如订单、用户 ID):🚀 Snowflake

大规模分布式(如日志、追踪 ID):✅ UUID

数据库存储主键:🎯 MySQL 自增

消息队列 / 任务 ID:🔥 Redis INCR

全局顺序 ID:📌 Zookeeper

如果你的业务是 高并发、分布式,推荐使用 Snowflake!🚀

附加问题(时钟回拨问题)

  1. 假如算法的时间比获取的机器时间久。这个时候如果时间回拨,会导致再生成的问题。

解决方案:

(1)如果当前时间大于时钟时间,那么就抛出异常。

(2)延迟等待

(3)增加时钟序列,如果有回拨,增加时钟序列号,这样如果是三位的,可以使用8次,然后生成的序列号也是可以自定义其中的位数。