分布式 ID 生成方式汇总

主题

分布式 ID 生成方式汇总

业务需求

  1. 需要不断产生唯一 ID,标记记录
  2. 需要在多个节点间使用,要求全局唯一

解决方案

数据库自增序列 ID

具体实现不多说

优点

缺点

UUID

具体实现不多说

优点

生成足够简单,本地生成无网络消耗,具有唯一性

缺点

  • 无序的字符串,不具备趋势自增特性

  • 没有具体的业务含义

  • 长度过长,36 位长度的字符串,存储以及查询对 MySQL 的性能消耗较大。

    MySQL 官方明确建议主键要尽量越短越好,作为数据库主键 UUID 的无序性会导致数据位置频繁变动,严重影响性能。

基于数据库多实例自增 ID

采用多台()服务器,每台有各自初始值:

获取时,

其中 值在集群中各自不同, 为机器数,这样,压力就分散了

优点

压力分散,速度可接受

缺点

对数据库单台压力还是很大,且形成对数据库的依赖

基于数据库分号段自增 ID

id biz_type max_id step version
1 101 1000 2000 0

核心思想,一次性取出一片,慢慢消费。消费完了再取。类似吃瓜子,抓一把,吃完再抓。

进一步细化,还可以分业务取。举例就是,葵瓜子、南瓜子、西瓜子,各分各的盘。

优点

相对速度,相对压力,都有所提升

缺点

数据库的压力还是很大

基于 Redis 模式 INCR

依靠 Redis 的原子性,可以通过 Redis 实现

优点

相对数据库,速度有提升

缺点

Redis 形成依赖

Twitter 的雪花

充分利用 64bit Long 的空间,将机房/机器/时间戳都打进去,高效。不依赖外部。

twitter-archive/snowflake https://github.com/twitter-archive/snowflake/tree/snowflake-2010

Snowflake 生成的是 Long 类型的 ID,一个 Long 类型占 8 个字节,每个字节占 8 比特,也就是说一个 Long 类型占 64 个比特。

Snowflake ID 组成结构:正数位(占 1 比特)+ 时间戳(占 41 比特)+ 机器ID(占 5 比特)+ 数据中心(占 5 比特)+ 自增值(占 12 比特),总共 64 比特组成的一个 Long 类型。

  • 第一个 bit 位(1bit):Javalong 的最高位是符号位代表正负,正数是 0,负数是 1,一般生成 ID 都为正数,所以默认为 0

  • 时间戳部分(41bit):毫秒级的时间,不建议存当前时间戳,而是用(当前时间戳 - 固定开始时间戳)的差值,可以使产生的 ID 从更小的值开始;41 位的时间戳可以使用 69 年,

  • 工作机器 id10bit):也被叫做workId,这个可以灵活配置,机房或者机器号组合都可以。

  • 序列号部分(12bit),自增值支持同一毫秒内同一个节点可以生成 4096ID

优点

  1. 快,性能相当好
  2. 无依赖,简单

缺点

若是时间回表,即时钟回拨,会出现重复。必须考虑这个问题。

百度(uid-generator

雪花改进版本,需要添加数据库

baidu/uid-generator https://github.com/baidu/uid-generator

优点

  1. 继承了雪花的优点
  2. 克服了雪花回表的问题,不基于“System.currentTimeMillis()”,而采用 AtomicLong 递增
  3. 还有 Cache 版本,使用 RingBuffer,克服伪共享问题

缺点

由于不是真实时间戳,所以出来的 ID,实际上不一定是 ID 中内嵌的时间

美团(Leaf

同时支持号段模式和雪花模式,可任选。雪花时,使用 ZooKeeper 作为辅助。

Meituan-Dianping/Leaf https://github.com/Meituan-Dianping/Leaf

备注

美团的雪花模式下,获取 IP 的逻辑直接取首个网卡 IP,用以当作 WorkerID。若是切换 IP,需要注意。

滴滴(Tinyid

支持号段模式

didi/tinyid https://github.com/didi/tinyid

备注

借鉴 Twitter/百度/美团的实现

小结

建议考虑使用雪花等对外部依赖没有或很低的 ID 生成方案