分布式ID生成方式

在分布式系统中,为了标识资源的唯一性,需要生成一个全局唯一的ID,插入数据库时好做区分。

常见的生成唯一ID的方式有:

  • uuid
  • 数据库自增ID
  • 号段模式
  • Redis
  • 类mongodbID

那么作为分布式系统中的ID,有哪些要求呢?

  1. 全局唯一
  2. 高性能&&高可用
  3. 趋势递增,有意义

其中uuid生成的虽然是唯一ID,但没有可读意义,也不适合放到数据库里作主键,不利于检索。

数据库自增ID

需要维护一张表来创建自增ID,当需要获取分布式ID时,往数据库里插入一条数据返回主键ID即可。
优点是实现简单,ID单调递增,数值类型查询快
缺点是DB单点存在宕机风险,也无法应对高并发场景

所有就有了升级版的数据库自增ID
在分布式数据库集群里,如果每个实例的自增ID都从1开始,还是会有重复ID,怎么办呢?
可以设置每个实例的起始ID不同,然后每个实例都次自增的步长一样:

1
2
3
4
5
6
7
// mysql实例1
set @@auto_increment_offset = 1; -- 起始值
set @@auto_increment_increment = 2; -- 步长

// mysql实例2
set @@auto_increment_offset = 2; -- 起始值
set @@auto_increment_increment = 2; -- 步长

以此类推即可,值得注意的是,如果发生了扩容,需要注意自增ID重复的情况

号段模式

号段模式是当下分布式ID生成器的主流实现方式之一,号段模式可以理解为从数据库批量的获取自增ID,每次从数据库取出一个号段范围,例如 (1,1000] 代表1000个ID,具体的业务服务将本号段,生成1~1000的自增ID并加载到内存。表结构如下:

1
2
3
4
5
6
7
8
CREATE TABLE id_generator (
id int(10) NOT NULL,
max_id bigint(20) NOT NULL COMMENT '当前最大id',
step int(20) NOT NULL COMMENT '号段的步长',
biz_type int(20) NOT NULL COMMENT '业务类型',
version int(20) NOT NULL COMMENT '版本号',
PRIMARY KEY (`id`)
)

由于多业务端可能同时操作,所以采用版本号version乐观锁方式更新,这种分布式ID生成方式不强依赖于数据库,不会频繁的访问数据库,对数据库的压力小很多。

Redis

利用Redis的inc命令实现ID的原子性自增

关注两点:

  1. 数据持久化(AOF && RDB)
  2. 高可用

类mongodbID

mongodb从一开始就设计用来做分布式数据库,为了生成文档的唯一性,objectID至关重要。

ObjectId是一个12字节的 BSON 类型字符串, 5f2cbaecc4cae51411eea431 。按照字节顺序,依次代表:

4字节:UNIX时间戳
3字节:表示运行MongoDB的机器
2字节:表示生成此_id的进程
3字节:由一个随机数开始的计数器生成的值

这样保证了分布式ID的唯一性。

类似的算法有:雪花算法