分布式ID生成方案总结整理

目录

  • 1、为什么需要分布式ID?
  • 2、业务系统对分布式ID有什么要求?
  • 3、分布式ID生成方案
    • 3.1 UUID
    • 3.2、数据库自增
    • 3.3、号段模式
    • 3.4、 Redis实现
    • 3.4、 雪花算法(SnowFlake)
    • 3.5、 百度Uidgenerator
    • 3.6、 美团Leaf
    • 3.7、 滴滴TinyID
1、为什么需要分布式ID?对于单体系统来说 , 主键ID可能会常用主键自动的方式进行设置,这种ID生成方法在单体项目是可行的,但是对于分布式系统,分库分表之后,就不适应了,比如订单表数据量太大了,分成了多个库 , 如果还采用数据库主键自增的方式,就会出现在不同库id一致的情况,虽然是不符合业务的
分布式ID生成方案总结整理

文章插图
2、业务系统对分布式ID有什么要求?
  • 全局唯一性:ID是作为唯一的标识,不能出现重复
  • 趋势递增:互联网比较喜欢MySQL数据库,而MySQL数据库默认使用InnoDB存储引擎,其使用的是聚集索引,使用有序的主键ID有利于保证写入的效率
  • 单调递增:保证下一个ID大于上一个ID,这种情况可以保证事务版本号,排序等特殊需求实现
  • 信息安全:前面说了ID要递增,但是最好不要连续,如果ID是连续的,容易被恶意爬取数据 , 指定一系列连续的 , 所以ID递增但是不规则是最好的
3、分布式ID生成方案
  • UUID
  • 数据库自增
  • 号段模式
  • Redis实现
  • 雪花算法(SnowFlake)
  • 百度Uidgenerator
  • 美团Leaf
  • 滴滴TinyID
3.1 UUIDUUID (Universally Unique Identifier),通用唯一识别码的缩写 。UUID的标准型式包含32个16进制数字,以连字号分为五段 , 形式为8-4-4-4-12的36个字符,示例: 863e254b-ae34-4371-87da-204b71d46a7b 。UUID理论上的总数为1632=2128,约等于3.4 x 10^38 。
  • 优点
    • 性能非常高,本地生成的,不依赖于网络
  • 缺点
    • 不易存储 , 16 字节128位 , 36位长度的字符串
    • 信息不安全,基于MAC地址生成UUID的算法可能会造成MAC地址泄露,暴露使用者的位置
    • uuid的无序性可能会引起数据位置频繁变动,影响性能
3.2、数据库自增在分布式环境也可以使用mysql的自增实现分布式ID的生成,如果分库分表了,当然不是简单的设置好auto_increment_incrementauto_increment_offset 即可,在分布式系统中我们可以多部署几台机器,每台机器设置不同的初始值 , 且步长和机器数相等 。比如有两台机器 。设置步长step为2,Server1的初始值为1(1,3,5,7,9 , 11…)、Server2的初始值为2(2 , 4,6,8,10…) 。这是Flickr团队在2010年撰文介绍的一种主键生成策略(Ticket Servers: Distributed Unique Primary Keys on the Cheap )
假设有N台机器,step就要设置为N,如图进行设置:
分布式ID生成方案总结整理

文章插图
这种方案看起来是可行的,但是如果要扩容,步长step等要重新设置 , 假如只有一台机器,步长就是1,比如1,2,3,4,5,6,这时候如果要进行扩容,就要重新设置,机器2可以挑一个偶数的数字 , 这个数字在扩容时间内,数据库自增要达不到这个数的,然后步长就是2,机器1要重新设置step为2,然后还是以一个奇数开始进行自增 。这个过程看起来不是很杂 , 但是,如果机器很多的话,那就要花很多时间去维护重新设置
【分布式ID生成方案总结整理】这种实现的缺陷:
  • ID没有了单调递增的特性,只能趋势递增 , 有些业务场景可能不符合
  • 数据库压力还是比较大,每次获取ID都需要读取数据库,只能通过多台机器提高稳定性和性能
3.3、号段模式这种模式也是现在生成分布式ID的一种方法,实现思路是会从数据库获取一个号段范围 , 比如[1,1000],生成1到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 '号段的布长',biz_type int(20) NOT NULL COMMENT '业务类型',version int(20) NOT NULL COMMENT '版本号',PRIMARY KEY (`id`))