日期:2026-07-11
标签:#面试 #八股 #后端 #系统设计 #场景题
一句话答案
常用方案有数据库号段、Redis 自增和 Snowflake;高吞吐场景通常用号段或 Snowflake,并针对时钟回拨、节点编号冲突和趋势递增做治理。
方案对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| UUID | 无中心、简单 | 128 位、无序、索引局部性差 | 不要求数字和有序 |
| 数据库自增 | 严格递增、简单 | 单点与吞吐瓶颈 | 小规模系统 |
| 号段模式 | 高吞吐、趋势递增、抗短时 DB 故障 | 可能跳号,需维护号段 | 通用业务 ID |
| Redis INCR | 原子、性能较高 | 依赖 Redis 持久化和高可用 | 中等规模计数 |
| Snowflake | 本地生成、高吞吐、64 位 | 时钟回拨、workerId 管理 | 大规模分布式系统 |
面试口语版
如果允许趋势递增和少量跳号,我优先考虑号段模式:数据库原子分配一段 ID,服务实例在内存中发号;当前号段消耗到阈值时异步加载下一段,数据库短时不可用也能继续服务。若追求完全去中心化可用 Snowflake,把时间戳、机房/机器号和序列号组成 64 位 ID,但必须通过注册中心分配 workerId,并处理时钟回拨。
关键细节
- 双 Buffer 号段避免切换时阻塞。
- Snowflake 时钟小幅回拨可等待,大幅回拨应拒绝启动、切换备用位或报警隔离。
- 对外不要暴露连续订单量时,可额外生成业务展示号,内部主键仍保持索引友好。
面试官追问
- Snowflake 同毫秒序列耗尽怎么办?
- workerId 如何防重复?
- 号段服务宕机会不会产生重复 ID?
- 为什么数据库主键偏好趋势递增?
面试官追问参考答案
1. Snowflake 同毫秒序列耗尽怎么办?
序列位能表示的数量用完后,生成器应等待到下一毫秒,不能让序列回绕产生重复。若持续达到上限,说明单节点吞吐超过设计容量,应增加序列位、增加 worker 节点或采用批量号段。
2. workerId 如何防重复?
由 ZooKeeper/etcd 等协调系统通过租约分配 workerId,并绑定实例身份;启动时必须成功领取且定期续租,失去租约就停止发号。静态配置也可以,但需要严格的资产管理和重复检测,容器动态扩缩容下风险更高。
3. 号段服务宕机会不会产生重复 ID?
只要数据库以事务原子增加最大值,每段范围不会重复;实例宕机只会浪费尚未使用的号码,造成跳号。重启后不能从本地旧游标继续发号,必须重新申请新号段;双 Buffer 也需把段边界和状态管理正确。
4. 为什么数据库主键偏好趋势递增?
B+Tree 聚簇索引中,趋势递增插入大多发生在叶子末尾,页分裂和随机 I/O 较少,缓存局部性更好。完全随机 ID 会在各页插入,引发频繁页分裂、碎片和缓存抖动;但绝对连续可能暴露业务量,因此可分离内部主键与外部展示号。