Skip to content

分布式ID、号段、Snowflake与UUID面试题

本页只放标准回答、追问和原理跳转。

1. 分布式 ID 需要满足哪些要求

标准回答: 首先是目标范围内唯一,其次根据业务选择吞吐、延迟、趋势递增、索引局部性、可用性、跨地域、寿命和安全属性。严格连续通常与缓存、高可用和事务回滚冲突,必须与全局唯一、趋势递增分开定义。

原理:ID 需求维度

2. 技术主键、业务单号和幂等键能否共用

标准回答: 不建议。技术主键服务数据库关联,业务单号面向用户和审计,幂等键代表稳定业务意图,TraceId 串联一次调用。它们的生命周期、可见性、安全和重复语义不同,一个请求可以同时拥有多个标识。

原理:标识类型

3. 数据库自增和 Sequence 为什么会跳号

标准回答: Sequence/自增值的分配通常不与业务事务回滚绑定,取号后事务失败也不会归还;Sequence Cache、实例异常和批量预取也会产生空洞。它们保证唯一分配,不保证业务号码连续。

原理:自增与 Sequence

4. Redis INCR 能否直接做全局 ID

标准回答: 可以用于特定规模的集中递增,但要评估单 key 热点、持久化、复制确认、主从切换、旧备份恢复和跨地域延迟。常用 INCRBY 批量领取号段降低远程频率,代价是节点故障后允许空洞。

原理:Redis 与 ZooKeeper 方案

5. 号段模式为什么性能高

标准回答: 数据库只在领取一批区间时参与,通过行锁或乐观更新原子增加 max_id;应用在内存中使用原子游标发放。双 Buffer 在当前段接近耗尽时预取下一段,降低切换毛刺。已分配区间不能回收,否则旧节点恢复会重复发号。

原理:号段模式数据库乐观号段 Demo

6. Snowflake 经典位布局是什么

标准回答: 常见 64 位布局是 1 个固定符号位、41 位相对毫秒时间、10 位 WorkerId 和 12 位毫秒内序列。41 位约 69.7 年,10 位支持 1024 个身份,12 位每毫秒 4096 个 ID。位宽可定制,但改变一项会压缩其他项。

原理:Snowflake 位布局

7. Snowflake 每秒一定能生成 409.6 万个 ID 吗

标准回答: 这是单 Worker 的位宽理论上限,即每毫秒 4096 个;真实吞吐还受同步锁、时钟读取、CPU、批量方式和调用链影响。序列在同一毫秒耗尽后必须等下一毫秒或失败,不能绕回零继续生成。

原理:生成流程

8. WorkerId 怎样分配才安全

标准回答: 可以静态配置、数据库注册、协调服务租约或 StatefulSet 序号,但必须保证地域、集群和进程生命周期内唯一,并检测冲突。WorkerId 不能在旧进程仍可能恢复时立即回收;IP/MAC 截断哈希也存在碰撞和容器复用风险。

原理:WorkerId 分配

9. Snowflake 时钟回拨怎样处理

标准回答: 短回拨可在 Deadline 和阈值内等待;长回拨应拒绝并告警,或使用经过证明不会冲突的节点空间/持久化逻辑时间。直接按较小时间继续生成,可能与同 WorkerId 的历史时间和序列组合冲突。

原理:时钟回拨Snowflake JDK 8 Demo

10. UUID v4、UUID v7 和 ULID 有什么区别

标准回答: UUID v4 主要是随机位,无中心但索引局部性差;UUID v7 按 RFC 9562 将 Unix 毫秒时间放在高位,具备时间排序属性;ULID 是独立的 128 位时间加随机标识和 Base32 表示,不是 UUID 版本。同一毫秒严格单调取决于生成器实现,Java 8 标准库只直接提供随机 UUID 常用能力。

原理:UUID 家族

11. 为什么随机 UUID 可能让 MySQL 主键写入变慢

标准回答: InnoDB 聚簇主键决定数据页组织,随机值把插入分散到不同叶子页,增加随机访问、页分裂和缓存失效;宽主键还会被二级索引携带,放大索引体积。趋势递增 ID 通常局部性更好,但在其他分布式存储中也要评估尾部热点。

原理:索引局部性

12. 线上出现 ID 冲突怎样排查

标准回答: 保存冲突 ID 和生成元数据;Snowflake 解码时间、WorkerId、序列并查回拨、同 WorkerId 多实例、重启和 GC;号段查区间分配记录和错误回收;Redis/Sequence 查备份恢复和计数回退。先隔离生成器,不能删除唯一约束掩盖问题。

原理:ID 故障 Runbook

13. Snowflake 为什么怕 WorkerId 重复

标准回答: Snowflake 的唯一性来自“时间戳 + WorkerId + 同毫秒序列”的组合。如果两个进程使用同一个 WorkerId,并且在同一毫秒都从相同序列开始发号,就可能生成完全相同的 ID。尤其是每次请求新建生成器、容器复制配置、租约过早回收、长 GC 后旧进程恢复,都可能让两个生成器在同一节点空间里并发工作。

追问方向:

追问回答要点
IP后几位能不能做WorkerId不可靠,容器IP复用、截断碰撞、多集群重名都会出问题
租约过期后能否立刻回收不能,旧进程可能只是长GC或网络分区,恢复后还会继续生成
怎么治理分配中心、启动代际、续租失败主动停机、冲突检测和告警

原理:WorkerId 分配

14. Snowflake 序列耗尽怎么办

标准回答: 经典 12 位序列同一毫秒最多 4096 个 ID,耗尽后不能把序列绕回 0 继续生成,否则会与同毫秒前面的 ID 重复。正确做法是等待下一毫秒,或者在业务可接受时返回失败、扩展序列位、拆分更多 Worker,但扩序列位会压缩时间位或节点位。

原理:Snowflake生成过程Snowflake JDK 8 Demo

15. 号段模式为什么允许跳号

标准回答: 号段一旦从数据库分配给某个实例,就不能因为实例宕机或业务回滚再回收。否则旧实例恢复、重复消费或本地缓存未清理时会把同一段再次发出去,造成 ID 冲突。号段模式保证全局唯一和高吞吐,不保证严格连续。

原理:号段模式常见错误

16. Snowflake 和号段模式怎么选

标准回答: 如果要求本地生成、极低延迟、注册中心或数据库短暂不可用时仍能发号,并且能治理 WorkerId 和时钟回拨,可以选 Snowflake 类;如果更看重可审计、趋势递增、集中控制、业务 Tag 隔离和跨语言接入,且能接受发号服务和数据库号段表,可以选号段模式。订单主键两者都常见,支付请求号更应以业务幂等键为准,不能只看技术 ID。

对比项Snowflake号段模式
远程依赖正常发号不访问远程切段时访问数据库或发号服务
最大风险时钟回拨、WorkerId冲突号段表不可用、错误回收区间
顺序性大体按时间趋势递增按号段趋势递增,跨实例不严格连续
审计性需记录WorkerId分配和生成元数据数据库分配记录天然更好查
适用场景高并发本地生成多业务Tag、集中管控、跨语言SDK

原理:商业选型生产级ID服务

17. 业务订单号可以直接用数据库自增 ID 吗

标准回答: 不建议直接暴露内部自增 ID。自增 ID 容易被枚举,可能泄露业务量和数据规模,也不能替代资源授权。商业系统通常内部用 bigint 主键,外部订单号单独生成,可包含日期、渠道、随机段或校验位;接口仍必须做租户和资源级权限校验。

原理:标识类型安全与隐私边界