JJava 知识库
JAVA INTERVIEW

高频面试题

架构进阶约 3 分钟

设计一个每天生成百亿 ID 的服务,要求趋势递增且不能重复,你会怎么选方案?

参考回答约 3 分钟 · 口语表达
先说结论

我会先定目标和容量,再拆核心链路:UUID 去中心化且简单,但较长、随机写入索引成本高;数据库或 Redis 自增容易理解但依赖中心服务;数据库号段把一段 ID 分配到应用内生成,减少远程访问;雪花算法把时间、节点和序列编码进整数,吞吐高且趋势递增,但依赖时钟与节点标识治理。

01

我不会直接画架构图,会先确认目标、规模和一致性要求。比较 UUID、数据库号段、Redis 自增和雪花算法的顺序性与可用性。UUID 去中心化且简单,但较长、随机写入索引成本高;数据库或 Redis 自增容易理解但依赖中心服务;数据库号段把一段 ID 分配到应用内生成,减少远程访问;雪花算法把时间、节点和序列编码进整数,吞吐高且趋势递增,但依赖时钟与节点标识治理。 没有方案同时天然满足绝对递增、无中心、无限可用和零协调,应按业务约束取舍。

02

容量有了以后,再把入口、核心处理和数据落点串起来。沿着「请求 ID → 选择时间/号段/随机源 → 生成节点与序列字段 → 校验唯一与时钟 → 传播到存储日志与链路」观察输入、状态与输出,这些阶段都可以从日志、指标或源码里验证。 请求 ID ID 需求先明确唯一范围、趋势递增、长度、信息泄漏、生成吞吐和多机房容灾。 选择时间/号段/随机源 Snowflake 用时间戳、节点号和序列,号段服务从数据库批量领取区间,UUID 则靠随机空间。 workerid 租约表:节点号唯一。 数据库 UNIQUE(id):最终碰撞裁决。 我会先确认请求实际走到了哪条路径,再用运行数据验证,不会只看类名或配置猜测。

03

关键参数要从峰值流量和资源上限反推。模拟时钟回拨、同 workerId 和单毫秒序列耗尽,统计停发/冲突。

04

正常链路之外,还要设计失败补偿和可验证的恢复流程。多个实例从环境变量读取相同 workerId,毫秒内序列也相同。改用租约式节点号分配、启动冲突检测和数据库唯一约束兜底后才安全。 workerId 重复:生成失败/等待数:workerId 租约+启动检测。 时钟回拨未处理:时钟偏差:回拨切逻辑时钟/隔离。 把趋势递增误认为全局严格连续:节点号租约冲突:存储保留唯一约束。 固定输入和基线 先在没有故障注入的环境执行上述配置,固定数据规模、并发度、运行时版本和预热时间。以「生成失败率」为主基线,记录值应满足「记录活动/稳态基线」;同时保存 生成失败/等待数、时钟偏差,使后续变化能够回到同一时间轴比较。

05

最后再讲扩容、成本和方案边界。方案:更适合的场景:主要收益:代价与边界。 数据库自增/号段:单库或可接受集中分配:简单/趋势递增:中心依赖或批次空洞。 Snowflake:高吞吐本地生成:趋势递增、无中心请求:时钟和节点号治理复杂。 UUID/ULID:跨环境独立生成:碰撞概率低、接入简单:索引局部性、长度或排序语义不同。 选型至少带上 峰值流量、数据规模、依赖容量、一致性目标和故障预算,并用上面的量化基线验证;未知数据应明确为待测假设。

方案主链路从目标到落地
01请求 ID
02选择时间/号段/随机源
03生成节点与序列字段
04校验唯一与时钟
05传播到存储日志与链路