分布式 ID 生成方式汇总
主题
分布式 ID
生成方式汇总
业务需求
- 需要不断产生唯一
ID
,标记记录 - 需要在多个节点间使用,要求全局唯一
解决方案
数据库自增序列 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
):Java
中long
的最高位是符号位代表正负,正数是0
,负数是1
,一般生成ID
都为正数,所以默认为0
。时间戳部分(
41bit
):毫秒级的时间,不建议存当前时间戳,而是用(当前时间戳 - 固定开始时间戳)的差值,可以使产生的ID
从更小的值开始;41
位的时间戳可以使用69
年,工作机器
id
(10bit
):也被叫做workId
,这个可以灵活配置,机房或者机器号组合都可以。序列号部分(
12bit
),自增值支持同一毫秒内同一个节点可以生成4096
个ID
优点
- 快,性能相当好
- 无依赖,简单
缺点
若是时间回表,即时钟回拨,会出现重复。必须考虑这个问题。
百度(uid-generator
)
雪花改进版本,需要添加数据库
baidu/uid-generator https://github.com/baidu/uid-generator
优点
- 继承了雪花的优点
- 克服了雪花回表的问题,不基于“
System.currentTimeMillis()
”,而采用AtomicLong
递增 - 还有
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
生成方案