分布式 ID 有哪些生成方案?雪花算法原理与缺陷是什么?

结论先行:分布式 ID 的核心诉求是全局唯一,多数场景还要求趋势递增与高性能。常见方案有 UUID、数据库自增、号段模式、Redis 自增和雪花算法。雪花算法是其中最经典的自研方案:64 位 long 拆成时间戳 + 机器号 + 序列号,不依赖外部存储、趋势递增,但存在时钟回拨导致 ID 重复的风险。

一、方案对比

方案唯一性有序性优点缺点
UUID全局唯一无序本地生成、零依赖长、无序,不适合做索引
数据库自增单库唯一递增实现简单单点瓶颈、有规律可被遍历
号段模式唯一递增批量取号性能好需维护发号服务高可用
Redis INCR唯一递增性能高依赖 Redis 可用性
雪花算法唯一(受时钟约束)趋势递增无中心、高性能时钟回拨会重复

二、雪花算法原理

  • 组成:1 位符号位 + 41 位毫秒时间戳 + 10 位机器号 + 12 位序列号,共 64 位;
  • 单机单毫秒可生成 4096 个 ID,实际可用约 69 年;趋势递增,适合作为数据库主键;
  • 时钟回拨缺陷:系统时钟回拨后,同一毫秒的序列号可能与历史 ID 冲突;若回拨跨毫秒,更可能直接重复;
  • 为什么不选 UUID 当主键:无序导致 B+ 树随机插入、页分裂频繁,长字符串还放大了索引体积;
  • 机器号拆分配置:10 位可拆成 5 位机房 + 5 位机器,便于按机房隔离与排障。

三、回拨与改进方向

// 雪花 ID 位运算示意:时间戳左移 22 位 | 机器号左移 12 位 | 序列号
long id = ((timestamp - epoch) << 22) | (workerId << 12) | sequence;
// 机器号通常由注册中心或配置中心下发,必须全局唯一
  • 简单兜底:记录上次生成的时间戳,检测到回拨时短暂等待时钟追平再发号;
  • 生产实践:业界成熟方案有美团 Leaf 的号段与雪花双模式、百度的 UidGenerator,一般做法是自研发号服务 + 时钟监控 + 回拨熔断;
  • 号段模式补充:一次批量取号放内存,宕机最多丢一个号段,用双缓冲预取可避免取号阻塞;
  • 组件化:ID 生成建议做成独立组件或服务,统一管理位段分配与时钟监控,避免各团队各自实现出现机器号冲突;
  • 面试提示:能补充"41 位时间戳可用约 69 年、初始纪元可配置"这类实现细节,比只背位结构得分更高。

常见追问 / 记忆点

  • 追问:雪花 ID 为什么适合做数据库主键?答:趋势递增减少页分裂,比 UUID 更利于 InnoDB 聚簇索引。
  • 追问:机器号怎么分配?答:可用 ZooKeeper/注册中心自动分配或配置文件静态指定,需保证全局唯一。
  • 追问:时钟回拨怎么彻底解决?答:严格场景用中心化发号(号段/数据库),或检测回拨后拒绝发号并告警。
  • 记忆点:41 位时间 + 10 位机器 + 12 位序列,趋势递增不递增细节;回拨是必考点。
笔记加载中…