分布式ID生成方案
朱门酒肉臭,路有冻死骨。
简介
对于单体项目,主键 ID 常用主键自动的方式进行设置。但是在分布式系统中,分库分表之后就不行了,如果还采用简单数据库主键ID自增的方式,就会出现同一ID在不同数据库的情况。常见分布式ID生成方案:UUID、号段模式、Redis 实现、雪花算法(SnowFlake)、滴滴 TinyID、百度 Uidgenerator、美团 Leaf等。
1、UUID
优点
- 性能优异,本地生成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 类型存储。
- 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大部分是连续的,容易被扫库、或者测算出订单量)
朱门酒肉臭
路有冻死骨