UUID、数据库自增、Redis、Snowflake、号段模式如何选择?
简化版
分布式 ID 方案要按场景选。UUID 本地生成、简单,但长且随机,不适合 MySQL 大表主键;数据库自增简单递增,但分布式扩展和高并发较弱;Redis INCR 简单高性能,但依赖 Redis 和主从一致性;Snowflake 本地生成、高性能、趋势递增,但依赖时钟和 workerId;号段模式性能高、趋势递增、不依赖时钟,但依赖数据库分配号段。没有绝对最优,核心看唯一性、性能、可用性、趋势递增和运维复杂度。
详细版
如果只是单库单表,数据库自增最简单。需要跨服务、跨系统随时生成唯一标识,且不作为数据库主键,UUID 很方便。需要简单递增业务单号,可以用 Redis INCR。需要极高性能、本地发号、数据库友好,Snowflake 常用。希望不依赖机器时钟,又想减少数据库压力,号段模式很适合。
选型时要看几个问题:ID 是否作为 MySQL 主键,是否要求趋势递增,峰值 QPS 多高,是否允许中心化依赖,是否多机房,能否接受时钟回拨风险,是否要对外暴露,是否需要隐藏业务量。
工程上通常还会内部 ID 和外部单号分离,内部追求性能和索引友好,外部追求可读、安全和业务含义。
完整版教学
一、先明确 ID 的使用场景
选 ID 方案前,第一步不是看算法,而是看 ID 用在哪里。数据库主键、订单号、请求 ID、消息去重 ID、TraceId、短链接编号,它们对 ID 的要求完全不同。数据库主键关注短、递增、索引友好;请求 ID 关注唯一和易生成;订单号还可能关注可读性和不可枚举。
如果不区分场景,就容易得出错误结论。比如 UUID 不适合做 MySQL 大表主键,但非常适合做 TraceId;数据库自增不适合跨库全局 ID,但单库主键非常好用。
二、UUID 适合什么
UUID 的最大优点是本地生成、无中心依赖、冲突概率极低。它适合跨系统唯一标识、请求链路、幂等键、文件对象名、离线生成 ID 等场景。服务不需要访问数据库或 Redis 就能生成,扩展性很好。
缺点是长、随机、可读性差。作为 InnoDB 聚簇主键时,会造成随机写入和索引膨胀。高并发核心业务表通常不推荐直接用 UUID 字符串做主键。如果要用,考虑 binary(16)、有序 UUID 或内部 bigint 加外部 UUID。
三、数据库自增适合什么
数据库自增适合单库单表、中小规模业务。它简单可靠、趋势递增、对索引友好,开发和运维成本低。很多系统初期直接使用自增主键是合理选择。
缺点是分布式扩展较弱。分库分表后会冲突;中心化发号会有单点和瓶颈;对外暴露连续 ID 会泄露业务量。可以通过步长、号段、独立发号库改善,但复杂度会上升。
四、Redis INCR 适合什么
Redis INCR 适合需要简单递增 ID 或业务单号的场景。比如按日期生成订单流水、活动编号、短链接序号。它实现简单、性能高、格式灵活。
缺点是依赖 Redis 可用性和主从一致性。主从异步复制下,如果主节点故障但增量未同步,可能出现计数回退风险。热点 key 和多机房也要额外设计。因此 Redis 发号适合中等规模和可控风险场景,不一定适合最核心、极高并发、跨地域强容灾场景。
五、Snowflake 适合什么
Snowflake 适合高并发、本地发号、趋势递增、需要 bigint ID 的场景。它不需要每次访问中心服务,性能非常高,对 MySQL 索引也比较友好。订单、消息、日志、分库分表主键都常见。
缺点是依赖系统时钟和 workerId 管理。时钟回拨、workerId 冲突、序列号耗尽、多机房部署都是工程风险。使用 Snowflake 一定要有时钟回拨处理、workerId 分配机制和监控。
六、号段模式适合什么
号段模式适合希望 ID 趋势递增、不依赖时钟、又不想每次访问数据库的场景。它通过数据库批量分配号段,应用本地发号,性能高且可控。Leaf segment 是典型方案。
缺点是依赖数据库分配号段,数据库长时间不可用后号段耗尽会影响业务;服务重启会浪费未用 ID;全局严格递增不保证。双 buffer、步长动态调整和数据库高可用是关键优化。
七、选型对比方法
可以从五个维度比较:唯一性、性能、可用性、趋势递增、复杂度。UUID 唯一性和可用性强,数据库友好性弱;数据库自增简单递增,扩展性弱;Redis 简单高性能但中心依赖;Snowflake 性能强但有时钟风险;号段模式折中稳定但依赖数据库分配。
面试时可以给出结论:如果是 MySQL 大表主键,优先趋势递增 bigint,比如 Snowflake 或号段;如果是 TraceId 或幂等键,UUID 可以;如果是简单业务流水,Redis 或数据库号段可以;如果是单库系统,数据库自增足够。
八、工程落地建议
很多成熟系统会把内部 ID 和外部单号分开。内部 ID 用 bigint,服务数据库主键、关联和分片;外部单号可以带日期、业务前缀、校验位或随机段,服务用户展示和客服查询。这样一个系统不必把所有需求压到同一个 ID 上。
还要保留数据库唯一约束作为最后防线。无论用什么发号算法,最终写入核心表时都应该有唯一索引。ID 重复不能只靠日志发现,必须让数据库阻止错误数据落地。
九、常见误区与追问
这道题不能只背概念,要把「ID 方案对比」放回真实分布式系统里解释:参与方是谁、状态怎么流转、失败后怎么恢复,以及它在一致性、性能、可用性之间做了什么取舍。
| 回答层次 | 要讲清的内容 | 容易漏掉的边界 |
|---|---|---|
| 核心结论 | ID 方案要按唯一性、趋势递增、性能、可用性、长度、时钟依赖和运维成本综合选择 | 不要停在名词解释 |
| 流程机制 | 列出业务要求 -> 排除不满足唯一性或吞吐的方案 -> 评估数据库索引友好性 -> 评估故障和时钟风险 -> 选择并设计降级 | 说明触发方、存储方、确认点和兜底 |
| 工程取舍 | 订单主键常选 Snowflake 或号段模式;链路 TraceId 可用 UUID;低并发单库可用数据库自增 | ID 方案没有全能答案,要在唯一性、性能、可读性、索引友好和运维复杂度之间取舍 |
ID 方案对比 面试拆解:
1. 列出业务要求
2. 排除不满足唯一性或吞吐的方案
3. 评估数据库索引友好性
4. 评估故障和时钟风险
5. 选择并设计降级
记忆钩子:先说明唯一性、趋势递增、吞吐、可用性和时钟依赖,再按方案取舍;回答时要紧扣「ID 方案对比」这道题,不要把相邻概念混成一段泛泛的分布式套话。
- 误区:雪花算法适合所有业务。 时钟敏感和 workerId 管理是它的代价。
- 误区:UUID 因为简单所以最适合主键。 随机且长,对关系库聚簇索引不友好。
- 误区:号段模式一定比雪花差。 号段不依赖时钟,趋势递增,适合很多高并发主键场景。
- 追问:数据库主键优先考虑什么? 唯一、短、趋势递增、写入性能和容量。
- 追问:跨机房如何选? 要考虑时钟、机房位、DB 跨域依赖和容灾切换。
- 追问:有没有完美方案? 没有,只有针对业务约束的取舍。
十、加强记忆
UUID 简单去中心但长且随机;数据库自增简单递增但分布式弱;Redis INCR 简单高性能但依赖 Redis;Snowflake 本地高性能趋势递增但怕时钟和 workerId;号段模式高性能不怕时钟但依赖数据库分配。选型先看场景:数据库主键、业务单号、请求 ID、消息去重,答案不一样。