发布时间:2022-12-19 文章分类:编程知识 投稿人:李佳 字号: 默认 | | 超大 打印

分布式ID生成方案

      朱门酒肉臭,路有冻死骨。

简介

对于单体项目,主键 ID 常用主键自动的方式进行设置。但是在分布式系统中,分库分表之后就不行了,如果还采用简单数据库主键ID自增的方式,就会出现同一ID在不同数据库的情况。常见分布式ID生成方案:UUID、号段模式、Redis 实现、雪花算法(SnowFlake)、滴滴 TinyID、百度 Uidgenerator、美团 Leaf等。

1、UUID

分布式ID生成方案

UUID(Universally Unique Identifier)长度是128bit位(16字节),换算为16进制数值(每4位代表一个数值)就是有32个16进制数值组成,中间使用4个-进行分隔,按照8-4-4-4-12的顺序进行分隔。加上中间的横杆,UUID有36个字符。比如:55d8cd15-7943-4849-89a8-ce7e7ce0b1d0。

优点

  • 性能优异,本地生成ID,不需要进行远程调用。

缺点

  • UUID的无序性,无法保证趋势递增。
  • UUID过长,不易存储,往往用字符串表示,作为主键建立索引查询效率低。

2、号段模式

实现思路是从数据库获取一个号段范围,比如 [1,1000],然后生成 1 到 1000 的自增 ID 加载到内存中,直到ID 都用完了,再去数据库获取。

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

再次获取号段范围,更改最大值。

update id_generator set max_id = #{max_id+step}, version = version + 1 where version = # {version} and biz_type = XXX
  • 优点:方案成熟、社区使用热度高如百度 Uidgenerator,美团 Leaf;
  • 缺点:分布式ID生成依赖于数据库。

3、Redis 实现

由于 Redis 单线程的特点,可以保证 ID 的唯一性和有序性。可以使用INCR 和 INCRBY 这样的自增原子命令实现Redis 分布式 ID 。

优点:Redis基于缓存性能搞,而且可以保证唯一性和有序性;

缺点:系统需要引入 Redis 组件,增大系统维护成本和复杂度。并发请求量上来后,就需要集群,并且需要分布式锁保证只有一个Client拿到锁生成ID。

4、雪花算法(SnowFlake)

雪花算法(Snowflake)是由 Twitter 开源的分布式 ID 生成算法,结构上:符号位+时间戳+工作进程位+序列号位,一个64bit的整数(8字节),正好为一个long类型数据,所以 Java 程序中一般使用 Long 类型存储。

分布式ID生成方案

  • 1bit sign:第一位占用 1 bit,始终是 0,是一个符号位,不使用;
  • 41bit timestamp:第 2 位开始的 41 位是时间戳。41-bit 位可表示 241 个数,每个数代表毫秒,那么雪花算法可用的时间年限是 (241)/(1000606024365)=69 年的时间,即从1970年开始,雪花算法能用到2039年;
  • 10bit workerId:10-bit 位可表示机器数,即 2^10 = 1024 台机器。通常不会部署这么多台机器;
  • 12bit sequence:12-bit 位是自增序列,表示每个机房的每个机器每毫秒可以产生2^12-1(4095)个不同的ID序号。

可以自己实现雪花算法,也可以使用一些封装好的比如hutool。

1 <dependency>
2     <groupId>cn.hutool</groupId>
3     <artifactId>hutool-core</artifactId>
4     <version>5.1.2</version>
5 </dependency>
6 
7 // 传入机器id和数据中心id,数据范围为:0~31
8 Snowflake snowflake = IdUtil.createSnowflake(1L, 1L);
9 System.out.println(snowflake.nextId()); 

优点

  • 雪花算法是目前解决分布式唯一ID的一种很好的解决方案,也是目前市面上使用较多的一种方案。
  • 雪花算法生成的 ID 是趋势递增,不依赖数据库等第三方系统。
  • 生成 ID 的效率非常高,稳定性好,可以根据自身业务特性分配 bit 位,比较灵活。

缺点

  • 雪花算法强依赖于机器时钟。如果机器上时钟回拨,会导致发号重复或者服务会处于不可用状态。如果恰巧回退前生成过一些 ID,而时间回退后,生成的 ID 就有可能重复。

5、百度 Uidgenerator

百度 UidGenerator 是百度开源基于 Java 语言实现的唯一 ID 生成器,是在雪花算法 Snowflake 的基础上做了一些改进。如:

  • 通过消费未来时间克服了雪花算法的并发限制。UidGenerator提前生成ID并缓存在RingBuffer中。 压测结果显示,单个实例的QPS能超过600万。雪花算法中sequence (13 bits):每秒下的并发序列,13 bits 可支持每秒 8192 个并发。
  • 支持自定义 workerId 位数和初始化策略,从而适用于 docker 等虚拟化环境下实例自动重启、漂移等场景。

6、美团 Leaf

目前主流的分布式ID生成方式大致都是基于数据库号段模式和雪花算法(snowflake),而美团(Leaf)刚好同时兼具了这两种方式,用户可以同时开启两种方式,也可以指定开启某种方式,能够根据不同业务场景灵活切换。

7、滴滴 Tinyid

Tinyid 是滴滴用 Java 开发的一款分布式 id 生成系统,基于数据库号段算法实现,Tinyid 扩展了 leaf-segment 算法,支持了多db(master),同时提供了 java-client(sdk) 使 id 生成本地化,获得了更好的性能与可用性。Tinyid 在滴滴客服部门使用,均通过 tinyid-client 方式接入,每天生成亿级别的 id。

使用注意事项

  • http 方式访问时,性能取决于 http server 的能力,网络传输速度。
  • java-client 方式访问时,id为本地生成,号段长度(step)越长,qps越大,如果将号段设置足够大,则qps可达1000w+。只要server有一台存活,则理论上可用,server全挂,因为client有缓存,也可以继续使用一段时间。
  • Tinyid 依赖db,当db不可用时,因为server有缓存,所以还可以使用一段时间,如果配置了多个db,则只要有1个db存活,则服务可用。
  • Tinyid 不适用场景:类似订单 id 的业务(因为生成的id大部分是连续的,容易被扫库、或者测算出订单量)

朱门酒肉臭

路有冻死骨