分布式id生成方式
为了解决在分布式系统中产生全局唯一id,且满足高并发、高可用的要求,区别于传统产生id的方式,介绍几种产生分布式id的方式。
1、分布式id需要解决的问题
- 全局唯一性
- 高性能,低延时
- 高可用
- 趋势递增
- 最好有一定的意义
2、生成分布式id的常用方式
- uuid
- 数据库自增
- 基于数据库集群
- 数据库段号模式
- Redis
- SnowFlake(雪花算法)
- TinyID(滴滴)
- UId-generator(百度)
- Leaf(美团)
基于UUID算法
产生唯一ID,最容易想到的就是UUID,36位的16进制的String字符串,可以通过四种不同的方式产生,基于时间的,基于名称的,随机的和DCE安全性的,分别提供了不同的方法。下面是UUID类中的一段注释
* <p> The version field holds a value that describes the type of this {
@code
* UUID}. There are four different basic types of UUIDs: time-based, DCE
* security, name-based, and randomly generated UUIDs. These types have a
* version value of 1, 2, 3 and 4, respectively.
使用随机的方式产生的uuid 为91a884cd-b65b-450e-9c43-a415a33e00b5
public static void main(String[] args) {
UUID uuid = UUID.randomUUID();
System.out.println(uuid);
}
UUID的优缺点
- 优点
- 直接可以在本地生成,没有网络I/O,性能较高
- 无序,无法预测顺序,且不包含意义
- 具有唯一性
- 缺点
- 36位的16进制,使用String存储,过长
- 没有自增的特性,无序
- 使用UUID生成的id在数据库中主键
UUID
的无序性会导致数据位置频繁变动,严重影响性能
使用的场景:不需要关注id的自增,且id不表示任何意义,对于存储的id字长不关注的情况,可以使用。
数据库自增
唯一标识,可以使用数据库中的唯一自增主键来实现,实现简单,只需要创建一张表。当需要一个ID
时,只需要插入一条记录返回主键ID
。这种方式有一个致命的缺点,当访问量激增时,mysql本身就是系统的瓶颈,使用它产生分布式系统中的ID
风险比较大。
优缺点
- 优点
- 实现简单,有序递增,数值类型查询速度快
- 缺点
- 并发性能不高,且受性于数据库的性能。
- 单点存在宕机的风险。
基于数据库集群
数据库自增模式存在单点宕机的风险,多数据库集群模式对此进行了优化。可以使用多个主节点,如果一个挂了,可以使用其他的主节点。
如果使用两个不同的数据库,是否有可能产生重复的自增ID
呢?可以通过设置不同的起始值和步长解决。比如,分别从 1、2、3开始,步长为3,则第二个ID
分别是4、5、6。
如果集群模式还是无法满足高并发的要求的性能,可以通过增加节点。但是增加节点之后需要重新设置起始值或者步长,这个比较麻烦。
优缺点
- 优点
- 解决单点宕机的风险
- 缺点
- 不利于扩容。
- 单个数据库性能的瓶颈可能无法满足高并发的场景
数据库的段号模式
对于数据库集群模式中出现的单个数据库依然无法解决高并发的要求,可以通过一次获取批量的id
,每次从数据库中取一个号段范围,比如一次(0,1000],取出1000个自增id
。可以创建如下的表
CREATE TABLE id_generator (
id int(10) NOT NULL,
max_id bigint(20) NOT NULL COMMENT '当前最大id',
step int(20) NOT NULL COMMENT '号段的布长',
type int(20) NOT NULL COMMENT '业务类型',
version int(20) NOT NULL COMMENT '版本号',
PRIMARY KEY (`id`)
)
其中版本号可以使用乐观锁,每次更新version,保证高并发时数据的正确性。
一批使用完之后,再次向数据库申请号段,对max-id
做一次更新操作&