目录
分布式id要考虑的问题
- 全局唯一
- 高可用:确保任何时候都能正确生成id
- 高性能:id生成响应要快、低延时,否则反倒会成为业务瓶颈
- 简单易用:在设计和实现上要尽可能的简单,拿来即用
- 是否需要是有序递增、需要包含日期时间等特殊部分:具体看业务场景
常见实现方案
- uuid
- 数据库主键自增
- 号段模式
- Redis
- 雪花算法snowflake
uuid
标准格式:以-将36个字符分为5段,xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12),包含
- 当前日期时间
- 时钟序列
- 全局唯一的IEEE机器识别号。如果有网卡,从网卡MAC地址获得,没有网卡以其他方式获得。
//根据需要去除-
String uuid = UUID.randomUUID().toString().replaceAll("-", "");
优点
- 本地生成,没有网络通信消耗,id生成性能高,没有高可用风险
- 简单易用
缺点
- 数据库主见字段长度越短越好,uuid长度太长,如果作为数据库主键、索引,查询效率低;在InnoDB引擎下,uuid的无序性会导致数据位置频繁变动,不易存储,存储、查询对数据库性能消耗较大,严重影响性能。
- 不能表示具体的业务含义
- 无序,不满足递增需求
- uuid中可能包含了mac地址,有mac地址泄露的风险
uuid一般用于生成文件名。
数据库主键自增
优点
- 实现简单、性能可以接受
- 有序递增
缺点
- 数据库常见架构是一主多从+读写分离,生成自增ID是写请求,主库故障时不能生成id,有单点故障风险,可用性低;主库的写性能决定id的生成性能,性能不高。
- 不同数据库实现方式不同,数据库迁移时需要额外处理。
- 分库分表时会有麻烦。
优化方案:双(多)主模式集群
id字段需要设置初始值、步长。
eg. 主库1的id初始值是1,主库2的id初始值是2,步长都是2,则主库1生成的id是1、3、5、7、9…,主库2生成的id是2、4、6、8、10…
优点
- 提升了可用性、解决了主库单点故障问题
- 多个主库,提升了生成id的性能
缺点
- 后续扩容麻烦,增加主库节点时需要修改其他主库的初始值、步长设置
号段模式
数据表 id_generator
字段 | 描述 |
---|---|
id | 这张表的主键字段 |
biz_type | 业务类型 |
max_id | 已使用的号段区间的最大值 |
step | 步长,即号段中id的数量 |
version | 版本号,用于实现乐观锁,更新此表时使用 |
初始记录示例
1 1 0 2000 0
1这种类型的业务申请号段时
-- 先查询此种业务类型已使用的最大id、步长、版本号,此次要申请的号段是(max_id,max_id+step)。
-- 0、2000 =》(0,0+2000】
select max_id, step, version from id_generator where biz_type=1;
-- 再更新此种业务类型对应的记录,version保证并发下的整个申请操作的原子性
update id_generator set max_id=#{max_id+step}, version=version+1 where biz_type=1 and version=#{version};
执行成功则申请到(0,2000】号段,将号段范围、当前使用到的id保存到内存中,后续直接使用申请到的这2000个id,用完再次申请即可。
优点
- 不强依赖于数据库,不会频繁访问数据库,对数据库的压力小
缺点
- 编码量大
借助redis的自增操作
Redis是单线程的,可以用Redis的原子操作 incr、incrby来实现分布式id。
这2个命令,如果key不存在,会先初始化为0,在执行incr操作。
可以用redis来生成每天从0开始的流水号,eg. 订单号=日期+当日自增长号,可以每天在redis中生成一个key,使用incr进行累加。
优点
- 不依赖数据库,灵活方便,且性能优于数据库
- 数字id,天然有序递增
缺点
- 需要引入redis,增加了编码量
- redis是内存数据库,rdb持久化方式可能发生数据丢失,造成id重复
优化方案:使用Redis集群获取更高的吞吐量
需要给redis各个节点设置初始值、步长。
雪花算法snowflake
snowflake是twitter开源的分布式ID生成算法,一些公司在其基础上进行了改良,先生成一个64位的二进制正整数,然后转换为10进制数。
64位的二进制数结构如下
- 1bit作为符号位,不用
- 41bit为毫秒数,可以记录69年
- 10bit为机器编号,可以记录1024台机器,一般用前5位代表数据中心,后面5位是某个数据中心的机器ID
- 12bit为毫秒内序列号,最多可以记录4095个
理论上单机每秒内最多可以生成1000*(2^12),也就是400W的ID,一般都能满足业务需求。
以上只是一个划分标准,不一定要这么做,可根据业务场景自行划分。
优点
- 简单高效,生成速度快
- 时间戳在高位,自增序列在低位,整个ID是趋势递增的,按照时间有序递增
- 灵活度高,可根据业务需求,调整bit位的划分
缺点
- 依赖机器时钟,在分布式环境上,每个服务器的时钟不可能完全同步,有时会出现不是全局递增的情况,如果服务器时钟回拨,会导致生成的id重复
百度开源的 uid-generator
基于snowflake实现,支持自定义bit位。
需要和数据库配合使用。
github地址:https://github.com/baidu/uid-generator
美团开源的Leaf
同时支持号段模式和snowflake算法模式,可以切换使用,其中号段模式需要借助数据库,snowflake需要借助zk。
github地址:https://github.com/Meituan-Dianping/Leaf
滴滴开源的tinyid
基于号段模式实现,依赖于数据库。
github地址:https://github.com/didi/tinyid