分布式ID生成策略

直接使用UUID生成全局ID有什么问题?

标准 UUID(UUID v4)格式:38400000-8cfa-432b-bcb2-2b96f8447889

由随机数生成,全局重复概率极低

存在的核心问题

1.无序,严重影响数据库索引性能

MySQL 主键使用 B + 树索引,索引有序时新数据追加在索引末尾,页分裂少、插入极快。 UUID 完全随机无序,新主键随机散落在索引各个位置:

更致命的是数据库性能:UUID 是 36 位字符串,而雪花算法是 64 位数字。InnoDB 索引对字符串的处理效率远低于数字 ------ 字符串索引需要逐字符比较,数字只需数值对比,相同数据量下,字符串索引查询速度慢 30% 以上。

  1. 频繁触发索引页分裂、页移动,插入速度大幅变慢;

  2. 索引磁盘碎片暴增,查询 IO 变高;

  3. 数据量越大,性能衰减越明显。

  4. 字符长度长,占用存储空间更大

  5. 主键本身占用更多磁盘;

  6. 其他表外键关联该主键时,跟着同步膨胀;

  7. 内存缓存索引、分页查询时内存开销更高。

3.使用UUID本身性能就较低

在 Java 环境下,生成 100 万条 ID,UUID v4 需要 128 毫秒,而雪花算法仅需 19 毫秒,速度差 7 倍

分布式ID生成5大方案

1.数据库自增ID

核心原理:建表时给ID字段加AUTO_INSREMENT,插入数据时数据库自动生成唯一ID,比如用户表ID字段,插一条张一条,无需额外代码

需要注意的点:

  • 分库分表防重复:单库自增没问题,多库分表会重复(比如表 1 和表 2 都从 1 开始自增)。解决方案是 "步长自增":给每个分表配置不同起始值和步长,比如表 1 从 1 开始、步长 2(1,3,5...),表 2 从 2 开始、步长 2(2,4,6...),确保 ID 不重复。
  • 防敏感信息泄露:自增 ID 是连续的(比如订单 ID 从 10086 涨到 10186,竞争对手能算出你一天卖 100 单)。解决办法:给 ID 加随机前缀,比如 10086→2340010086(前缀 "234" 是固定随机数),既保留有序性,又隐藏真实订单量。

2.雪花算法

核心原理: Twitter 开源的分布式 ID 算法,用 64 位长整型(BIGINT)拆分为 3 部分,确保全局唯一且有序:

  • 41 位时间戳:记录毫秒级时间(从自定义起始时间开始算),可支撑 69 年(2^41 / 1000 / 60 / 60 / 24 / 365 = 69);
  • 10 位机器 ID:标识不同服务器(2^10=1024),支持 1024 台机器集群;
  • 12 位序列号:同一毫秒内生成的 ID 序号(2^12=4096),单台机器每秒可生成 409.6 万 ID。

64 位数字 ID 结构: 1位 (位符) | 41 位(时间戳 ms) | 10 位(机器 ID) | 12 位(序列号) |

实操细节 & 避坑点

  • 时钟回拨问题(最致命):服务器时钟突然回拨(比如 NTP 同步时时间跳回 300 毫秒),会导致生成重复 ID。 解决方案: a. 记录每台机器最后一次生成 ID 的时间; b. 若当前时间戳 < 最后一次时间戳,说明发生回拨,暂停生成 ID,等待时钟追上最后一次时间戳(比如回拨 300 毫秒,就等 300 毫秒再生成); c. 若回拨时间超过阈值(比如 1 秒),直接报错,避免长时间阻塞。
  • 机器 ID 分配:手动给每台机器配 ID 容易出错,可用 ZooKeeper 自动分配 ------ 机器启动时向 ZooKeeper 申请唯一 ID(如节点 /snowflake/machineId/ 下的自增节点),退出时释放 ID,支持动态扩容。

3.美团Leaf

核心原理:美团开源的分布式 ID 生成框架,基于雪花算法优化,支持两种模式:雪花模式(解决时钟回拨)和号段模式(降低数据库依赖),适合中大型系统。

两种模式详解

  1. 号段模式(性能优先)
  • 原理:提前从数据库拿一段 ID(比如 "1-1000")存到内存,业务生成 ID 时直接从内存取,用完后再去数据库拿下一段("1001-2000");
  • 优势:减少数据库访问(内存生成),即使数据库宕机,内存里的号段仍能支撑一段时间,容灾性强;
  • 注意:服务器宕机时,未用完的号段会丢失(比如 "1-1000" 只用了 500 个,宕机后重启拿 "1001-2000"),ID 会断号,但大部分业务(如订单)不关心连续性,可接受。
  1. 雪花模式(可靠优先)
  • 优化点:机器 ID 由 ZooKeeper 自动分配,无需手动配置;时钟回拨时,短回拨(<5 毫秒)等待,长回拨(>5 毫秒)直接报错,避免重复;
  • 性能:单机 QPS 达 10 万 +,集群 QPS 达 100 万 +,满足大促需求。

4.UUID v7

核心原理:针对 UUID v4 的无序问题优化,在 128 位 ID 中加入时间戳:

  • 前 48 位:毫秒级时间戳(从 1970-01-01 开始),确保 ID 按时间有序;
  • 后 80 位:随机数(保证唯一性);
  • 格式:兼容 UUID v4(36 位字符串),可直接替换旧系统的 UUID。

实操细节 & 避坑点

  • 语言支持:Java JDK17 及以下不原生支持 UUID v7,需用第三方库(如 com.fasterxml.uuid:java-uuid-generator:4.0.1);
  • 性能上限:虽比 v4 有序,但仍是字符串,生成速度和索引性能不如雪花算法(实测生成 100 万 ID 需 80 毫秒,比雪花算法慢 4 倍)。

5.Redis自增ID

核心原理

利用 Redis 的 INCR 命令(原子性自增)生成 ID,比如定义键 order:id,每次调用 INCR order:id,返回的结果就是唯一 ID(从 1 开始递增)。

实操细节 & 避坑点

  • 高可用保障:Redis 单点故障会导致 ID 生成不可用,需部署主从集群 + 哨兵模式,确保 Redis 宕机时自动切换;
  • 持久化防丢失:开启 AOF(Append Only File)+ RDB(快照)持久化,避免 Redis 重启后 ID 重置(比如 order:id 涨到 10 万,重启后变回 1);
  • 性能优化:高并发时可批量生成 ID(比如一次 INCRBY order:id 1000 拿 1000 个 ID 存内存),减少 Redis 访问。
相关推荐
不会代码的小猴32 分钟前
7. JSON
开发语言·c++·笔记·qt·算法·json
用户37215742613533 分钟前
如何使用 Java 在 Word 文档中添加和删除水印:分步指南
java
怪奇云呼军1 小时前
知识库也会注入指令?闪电智能VoiceAgent 如何防住 Prompt Injection
人工智能·python·算法·云计算·音视频
王中阳Go2 小时前
老板用AI三天写到90%,让我明天上线:Java 团队怎么接这10%的烂摊子?
java
阿里云大数据AI技术2 小时前
基于 EMR Serverless Ray 实现 Qwen 模型批量推理实践
人工智能·算法·agent
gugucoding2 小时前
55. 【Java】Maven:项目构建的“管家”
java·maven
Rambo.xia2 小时前
为什么去马赛克算法,决定了ISP的画质上限
算法·接口隔离原则
Benny_Tang3 小时前
题解:P10230 [COCI 2023/2024 #4] Lepeze
c++·算法
Geek-Chow3 小时前
06 训练管线:数据如何变成权重
人工智能·算法