分布式ID生成方案
创始人
2024-05-25 14:56:49
0

文章目录

  • 前言
  • 一、分布式ID需要满足的条件
  • 二、分布式ID生成方式
    • 基于UUID
    • 数据库自增
    • 数据库集群
    • 数据库号段模式
    • redis ID生成
    • 基于雪花算法(Snowflake)模式
    • 百度(uid-generator)
    • 美团(Leaf)
    • 滴滴(Tinyid)

前言

对于单体系统来说,主键ID可能会常用主键自动的方式进行设置,这种ID生成方法在单体项目是可行的,但是对于分布式系统,分库分表之后,就不适应了,比如订单表数据量太大了,分成了多个库,如果还采用数据库主键自增的方式,就会出现在不同库id一致的情况。

在这里插入图片描述

一、分布式ID需要满足的条件

① 全局唯一:必须保证ID是全局性唯一的。
② 趋势有序:业务上分页查询需求,排序需求,如果ID直接有序,则不必建立更多的索引,增加查询条件。
而且Mysql InnoDB存储引擎主键使用聚集索引,主键有序则写入性能更高。
③ 高可用:ID是一条数据的唯一标识,如果ID生成失败,则影响很大,业务执行不下去。所以好的ID方案需要有高可用。
④ 信息安全:ID虽然趋势有序,但是不可以被看出规则,免得被爬取信息。

二、分布式ID生成方式

今天主要分析一下以下9种,分布式ID生成器方式以及优缺点:

  • UUID
  • 数据库自增ID
  • 数据库多主模式
  • 号段模式
  • Redis
  • 雪花算法(SnowFlake)
  • 滴滴出品(TinyID)
  • 百度 (Uidgenerator)
  • 美团(Leaf)

注:主流生成ID方案都是基于数据库号段模式和雪花算法

在这里插入图片描述

基于UUID

UUID (Universally Unique Identifier),通用唯一识别码的缩写。UUID的标准型式包含32个16进制数字,以连字号分为五段,形式为8-4-4-4-12的36个字符,示例: 863e254b-ae34-4371-87da-204b71d46a7b。

String uuid = UUID.randomUUID().toString().replaceAll("-","");
System.out.println(uuid);// 9c58226555c248018be2032964de2de6

优点:

  • 性能非常高,本地生成的,不依赖于网络。

缺点:

  • 无序。
  • 不能标识出此ID的含义,不可读。
  • 字符串太长且无序,作为MySQL主键,影响性能。

数据库自增

基于数据库的 auto_increment 自增ID完全可以充当分布式ID。

优点:

  • 实现起来比较简单,ID 有序递增,存储消耗空间小。

缺点:

  • 存在数据库单点问题(可以使用数据库集群解决,不过增加了复杂度)。
  • ID 没有具体业务含义。
  • 安全问题(比如根据订单 ID 的递增规律就能推算出每天的订单量)。
  • 每次获取 ID 都要访问一次数据库(增加了对数据库的压力,获取速度也慢)。
  • 分库分表后,同一数据表的自增ID容易重复,无法直接使用(可以设置步长,但局限性很明显),ID没有了单调递增的特性,只能趋势递增,有些业务场景可能不符合。

数据库集群

前边说了单点数据库方式不可取,那对上边的方式做一些高可用优化,换成主从模式集群。害怕一个主节点挂掉没法用,那就做双主模式集群,也就是两个Mysql实例都能单独的生产自增ID。

设置起始值自增步长

MySQL_1 配置:

set @@auto_increment_offset = 1;     -- 起始值
set @@auto_increment_increment = 2;  -- 步长

MySQL_2 配置:

set @@auto_increment_offset = 2;     -- 起始值
set @@auto_increment_increment = 2;  -- 步长

这样两个MySQL实例的自增ID分别就是:

1、3、5、7、9
2、4、6、8、10

水平扩展的数据库集群,有利于解决数据库单点压力的问题,同时为了ID生成特性,将自增步长按照机器数量来设置。
增加第三台MySQL实例需要人工修改一、二两台MySQL实例的起始值和步长,把第三台机器的ID起始生成位置设定在比现有最大自增ID的位置远一些,但必须在一、二两台MySQL实例ID还没有增长到第三台MySQL实例的起始ID值的时候,否则自增ID就要出现重复了,必要时可能还需要停机修改。

优点:

  • 解决DB单点问题

缺点:

  • 不利于后续扩容。
  • 实际上单个数据库自身压力还是大,依旧无法满足高并发场景。

数据库号段模式

这种模式也是现在生成分布式ID的一种方法,实现思路是会从数据库获取一个号段范围,比如[1,1000],生成1到1000的自增ID加载到内存中,建表结构如:

CREATE TABLE `sequence_id_generator` (`id` int(10) NOT NULL,`current_max_id` bigint(20) NOT NULL COMMENT '当前最大id',`step` int(10) NOT NULL COMMENT '号段的长度',`version` int(20) NOT NULL COMMENT '版本号',`biz_type`    int(20) NOT NULL COMMENT '业务类型',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

current_max_id 字段和 step 字段主要用于获取批量 ID,id 为: current_max_id ~ current_max_id + step
version 字段主要用于解决并发问题(乐观锁),biz_type 主要用于表示业务类型。

① 先插入一行数据

INSERT INTO `sequence_id_generator` (`id`, `current_max_id`, `step`, `version`, `biz_type`) VALUES(1, 0, 100, 0, 101);

② 通过 SELECT 获取指定业务下的批量唯一 ID

SELECT `current_max_id`, `step`,`version` FROM `sequence_id_generator` where `biz_type` = 101

③ 不够用的话,更新之后重新 SELECT 即可。

UPDATE sequence_id_generator SET current_max_id = 0+100, version=version+1 WHERE version = 0  AND `biz_type` = 101
SELECT `current_max_id`, `step`,`version` FROM `sequence_id_generator` where `biz_type` = 101

相比于数据库主键自增的方式,数据库的号段模式对于数据库的访问次数更少,数据库压力更小。

另外,为了避免单点问题,你可以从使用主从模式来提高可用性。

优点:

  • ID 有序递增,存储消耗空间小,有比较成熟的方案,像百度Uidgenerator,美团Leaf

缺点:

  • 依赖于数据库实现。

redis ID生成

Redis分布式ID实现主要是通过提供像 INCRINCRBY 这样的自增原子命令,由于Redis单线程的特点,可以保证ID的唯一性和有序性。

这种实现方式,如果并发请求量上来后,就需要集群,不过集群后,又要和传统数据库一样,设置分段和步长。

时间+用redis的incr自增命令(每日从1开始),代码如下:

public class RedisCounterRepository {private final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");private RedisTemplate redisTemplate;@Autowiredpublic RedisCounterRepository(RedisTemplate redisTemplate) {this.redisTemplate = redisTemplate;}// 根据获取的自增数据,添加日期标识构造分布式全局唯一标识,changeNumPrefix是自己定义的随机前缀private String getNumFromRedis(String changeNumPrefix) {String dateStr = LocalDate.now().format(dateTimeFormatter);Long value = incrementNum(changeNumPrefix + dateStr);//不足4位补0,redis从1开始生成的,每天再次请0return dateStr + StringUtils.leftPad(String.valueOf(value), 4, '0');}// 从redis中获取自增数据(redis保证自增是原子操作)private long incrementNum(String key) {RedisConnectionFactory factory = redisTemplate.getConnectionFactory();if (null == factory) {log.error("Unable to connect to redis.");throw new UserException(AppStatus.INTERNAL_SERVER_ERROR);}RedisAtomicLong redisAtomicLong = new RedisAtomicLong(key, factory);long increment = redisAtomicLong.incrementAndGet();if (1 == increment) {// 如果数据是初次设置,需要设置超时时间redisAtomicLong.expire(1, TimeUnit.DAYS);}return increment;}
}

用redis实现需要注意一点,要考虑到redis持久化的问题。redis有两种持久化方式RDBAOF

  • RDB会定时打一个快照进行持久化,假如连续自增但redis没及时持久化,而这会Redis挂掉了,重启Redis后会出现ID重复的情况。
  • AOF会对每条写命令进行持久化,即使Redis挂掉了也不会出现ID重复的情况,但由于incr命令的特殊性,会导致Redis重启恢复的数据时间过长。

优点:

  • 性能不错、每秒10万并发量。
  • 生成的 ID 是有序递增的

缺点:

  • redis 宕机后不可用,RDB重启数据丢失会重复ID。
  • 自增,数据量易暴露。

基于雪花算法(Snowflake)模式

根据这个算法的逻辑,只需要将这个算法用Java语言实现出来,封装为一个工具方法,那么各个业务应用可以直接使用该工具方法来获取分布式ID,只需保证每个业务应用有自己的工作机器id即可,而不需要单独去搭建一个获取分布式ID的应用。

https://blog.csdn.net/yy139926/article/details/128468074

优点:

  • 雪花算法生成的ID是趋势递增,不依赖数据库等第三方系统,生成ID的效率非常高,稳定性好,可以根据自身业务特性分配bit位,比较灵活。

缺点:

  • 每台机器的时钟不同,当时钟回拨可能会发生重复ID。
  • 当数据量大时,需要对ID取模分库分表,在跨毫秒时,序列号总是归0,会发生取模后分布不均衡。

如何解决时间回拨问题

时间回拨是指,当机器出现问题,时间可能回到之前,此时雪花算法生成的id可能与之前的id值相同,从而导致id重复。

  1. 系统抛出异常,运维来手动调整时间。
  2. 延迟等待,对于偶然性的时间回拨,也许是机器出现了一次小故障,频繁出现的概率并不大,所以对于这种情况没必要中断业务,可以采用阻塞线程5ms,再获取时间,对比看时间是否比上一次请求的时间大,如果大了,说明恢复正常了,则不用管;如果还小,说明真出问题了,则抛出异常,呼唤程序员处理。
  3. 备用机方式来解决,当前机器出现问题,迅速换一台机器,通过高可用解决。

百度(uid-generator)

美团(Leaf)

https://blog.csdn.net/yy139926/article/details/126740614

滴滴(Tinyid)

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
一帆风顺二龙腾飞三阳开泰祝福语... 本篇文章极速百科给大家谈谈一帆风顺二龙腾飞三阳开泰祝福语,以及一帆风顺二龙腾飞三阳开泰祝福语结婚对应...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...