在 分布式系统中生成唯一 ID,必须满足 全局唯一性、高性能、可扩展性 的要求。以下是几种常见的方案:
-
Snowflake 算法(Twitter 雪花算法)🔥
适用于 高并发分布式系统,每个 ID 是 64 位整数,格式如下:
💡 生成逻辑
-
当前时间戳(41 bit)
-
• 41 bit 可表示 69 年(2^41 / (1000606024≈ 69)。
-
机器 ID(10 bit)
• 通常 5 bit 数据中心 + 5 bit 机器 ID,总计支持 1024 台服务器。
- 毫秒内序列号(12 bit)
• 同一毫秒内最多 4096 个 ID,如果超过,则等待下一毫秒。
- 最终拼接成 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 同步时钟)。
-
UUID(通用唯一标识符)
💡 生成逻辑
UUID 是 128 位字符串,常见格式:
550e8400-e29b-41d4-a716-446655440000
Python 生成 UUID:
import uuid
print(uuid.uuid4()) # 生成随机 UUID
✅ 优势
• 去中心化,不依赖数据库或中心节点。
• 可离线生成,适合分布式环境。
❌ 缺点
• ID 太长(128 bit) ,存储效率低。
• 无序性,不适合数据库索引(插入性能差)。
-
数据库自增 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。
-
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 服务器,有一定部署成本。
-
基于 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)如果当前时间大于时钟时间,那么就抛出异常。
(2)延迟等待
(3)增加时钟序列,如果有回拨,增加时钟序列号,这样如果是三位的,可以使用8次,然后生成的序列号也是可以自定义其中的位数。