分布式 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 生成方案