分布式ID生成方法的超详细分析(全)

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 目录前言1. UUID2. 数据库自增3. 数据库集群4. 数据库号段5. redis模式6. 雪花算法7. 其他总结前言关于什么是分布式ID数据量不是很多的时候,单一个数据库表可以支撑其业务,即使数据在大也可以主从复制到一定量的数据时,实现分库分表的时候,就需要一个全局唯一的ID,订单的编号就是分布式ID关于上面牵扯到的主从复制可看我之前的文章进行查缺补漏关于主从复制的超详细解析(全)关于数据库的分布式ID可看我之前在Mycat种提及到具体都有如下:在实现分库分表的情况下,数据库自增主

前言

关于什么是分布式ID

数据量不是很多的时候,单一个数据库表可以支撑其业务,即使数据在大也可以主从复制
到一定量的数据时,实现分库分表的时候,就需要一个全局唯一的ID,订单的编号就是分布式ID

关于上面牵扯到的主从复制
可看我之前的文章进行查缺补漏
关于主从复制的超详细解析(全)

关于数据库的分布式ID可看我之前在Mycat种提及到

具体都有如下

在实现分库分表的情况下,数据库自增主键已无法保证自增主键的全局唯一。为此,Mycat 提供了全局 sequence

  • 本地文件

优点:本地加载,读取速度较快
缺点:抗风险能力差,Mycat 所在主机宕机后,无法读取本地文件

  • 数据库方式

利用数据库一个表 来进行计数累加。但是并不是每次生成序列都读写数据库,这样效率太低。Mycat 会预加载一部分号段到 Mycat 的内存中,这样大部分读写序列都是在内存中完成的。如果内存中的号段用完了 Mycat 会再向数据库要一次

  • 时间戳方式

全局序列ID= 64 位二进制 (42(毫秒)+5(机器 ID)+5(业务编码)+12(重复累加) 换算成十进制为 18 位数的
long 类型,每毫秒可以并发 12 位二进制的累加。
优点:配置简单
缺点:18 位 ID 过长

而涉及到程序上实现的分布式ID可看下面的文章
可通过UUID、雪花算法等

1. UUID

具体UUID是什么以及可看我之前的文章
java之UUID.randomUUID().toString()详细解析(全)
这里直接push过来

UUID 是 通用唯一识别码(Universally Unique Identifier)的缩写,主要是让让分布式系统中的所有元素,都能有唯一的辨识信息。

  • 为了提高效率,常用的UUID可缩短至16位比特数值
  • 使用UUID的一个好处是可以为新的服务创建新的标识符

==源码==
生成的UUID标识符16位具体信息如下:

  • (1)当前日期和时间,值第一部分与时间有关(如果你在生成一个UUID之后,过几秒又生成一个UUID,则第一个部分不同,其余相同)
  • (2)时钟序列
  • (3)全局唯一的IEEE机器识别号(如果有网卡,从网卡MAC地址获得,没有网卡以其他方式获得)
 public static UUID randomUUID() {
       
        SecureRandom ng = Holder.numberGenerator;
        byte[] randomBytes = new byte[16];
        ng.nextBytes(randomBytes);
        randomBytes[6]  &= 0x0f;  /* clear version        */
        randomBytes[6]  |= 0x40;  /* set to version 4     */
        randomBytes[8]  &= 0x3f;  /* clear variant        */
        randomBytes[8]  |= 0x80;  /* set to IETF variant  */
        return new UUID(randomBytes);
}

==通过randomUUID().toString()生成==
随机生成UUID的标识符是UUID类中的方法
而UUID.randomUUID().toString()是javaJDK提供的一个自动生成主键的方法

public class ThreadDemo4 {
    public static void main(String[] args) {
        //创建ArrayList集合
      List<String> list = new ArrayList<>();

        for (int i = 0; i <30; i++) {

            new Thread(()->{
                //向集合添加内容
                list.add(UUID.randomUUID().toString());
                //从集合获取内容
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}

代码截图
在这里插入图片描述

2. 数据库自增

前言中也有提及到数据库的方式,但是mycat的方式是放一定的序列到内存中,如果宕机之后,前面的序列不会更改,但是刚开始的序列号和之前的序列号会断开,也可以保证高可用

这种数据库的自增是通过数据库种的auto_increment,

用它来实现分布式服务风险比较大

3. 数据库集群

改进上面单个数据库自增的模式,因为怕宕机
那就多加入一个从机,实现主从复制或者是双主模式等

具体的规则就是设置一个起始值和步长值,实现两个mysql的不一样值
具体设置的方法为:

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

但是如果突然加进来第三个服务器,前面两个序列号就都要重改,也比较麻烦

4. 数据库号段

从数据库批量的获取自增ID,每次从数据库取出一个号段范围,生成的自增ID加载到内存中

这与一开始前言所提及到的mycat比较相像
但是mycat是已经把一部分号段放置到内存中
而这里是在数据库取一部分,再放置到内存中,逻辑不一样

加入号段之后,在每个号段还要加上乐观锁(在数据库表中增加一个version字段),防止高并发的数据量
关于这部分乐观锁可看我之前的文章补充一些知识点,这里就不再赘述
java中各类锁的机制详细解析(全)

不会频繁的访问数据库,对数据库的压力小很多,因为有了缓存在内存中了

5. redis模式

利用redis的 incr命令实现ID的原子性自增

考虑到redis持久化的问题。redis有两种持久化方式RDB和AOF:

(1)RDB会定时打一个快照进行持久化,假如连续自增但redis没及时持久化,而这会Redis挂掉了,重启Redis后会出现ID重复的情况。

(2)AOF会对每条写命令进行持久化,即使Redis挂掉了也不会出现ID重复的情况,但由于incr命令的特殊性,会导致Redis重启恢复的数据时间过长。

6. 雪花算法

指定机器 & 同一时刻 & 某一并发序列,是唯一的。据此可生成一个64 bits的唯一ID(long)

组成部分(64bit)
1.第一位 占用1bit,其值始终是0,没有实际作用。
2.时间戳 占用41bit,精确到毫秒,总共可以容纳约69年的时间。
3.工作机器id 占用10bit,其中高位5bit是数据中心ID,低位5bit是工作节点ID,做多可以容纳1024个节点。
4.序列号 占用12bit,每个节点每毫秒0开始不断累加,最多可以累加到4095,一共可以产生4096个ID。

SnowFlake算法在同一毫秒内最多可以生成多少个全局唯一ID呢: 同一毫秒的ID数量 = 1024 X 4096 = 4194304

==最核心的是中间的10位工作机器ID的分配,做到自动生成workID,避免运维人员的去分配==

关于雪花算法详细分析可看这篇文章
理解分布式id生成算法SnowFlake

7. 其他

关于其他的自增,分布式ID,各大公司都有其一套的的算法
比如:

  • 滴滴出品(TinyID)

数据库中保存了可用的id号段,tinyid会将可用号段加载到内存中,之后生成id会直接内存中产生

  • 百度 (Uidgenerator)

百度的雪花算法是通过数据库来生成workID

  • 美团(Leaf)

美团是通过Zookeeper持久顺序节点的特性自动对snowflake节点配置wokerID

总结

方法 优点 缺点
UUID 1.容易实现,产生快。2.ID唯一。3.无需要中心化服务器。4.不会泄露商业机密 1.可读性过差。2.占用空间过多。3.影响数据库性能
数据库自增 实现简单,ID单调自增,数值类型查询速度快。 单点存在宕机风险,无法扛住高并发场景。
数据库集群 解决DB单点问题 不利于后续扩容,单点压力也会更大不适合高并发
数据库号段 解决单点压力 抗风险能力差,没有集群
redis中的icncr 原子性自增保证不重复 RDB挂掉后出现重复,AOF挂掉重启时间长
雪花算法 所有生成的id按时间趋势递增。不依赖数据库等第三方系统,以服务的方式部署,稳定性更高,生成ID的性能也是非常高的。可以根据自身业务特性分配bit位,非常灵活。 强依赖机器时钟,如果机器上时钟回拨,会导致发号重复或者服务会处于不可用状态。
相关文章
|
25天前
|
机器学习/深度学习 监控 算法
分布式光伏储能系统的优化配置方法(Matlab代码实现)
分布式光伏储能系统的优化配置方法(Matlab代码实现)
|
5月前
|
消息中间件 运维 Kafka
直播预告|Kafka+Flink双引擎实战:手把手带你搭建分布式实时分析平台!
在数字化转型中,企业亟需从海量数据中快速提取价值并转化为业务增长动力。5月15日19:00-21:00,阿里云三位技术专家将讲解Kafka与Flink的强强联合方案,帮助企业零门槛构建分布式实时分析平台。此组合广泛应用于实时风控、用户行为追踪等场景,具备高吞吐、弹性扩缩容及亚秒级响应优势。直播适合初学者、开发者和数据工程师,参与还有机会领取定制好礼!扫描海报二维码或点击链接预约直播:[https://developer.aliyun.com/live/255088](https://developer.aliyun.com/live/255088)
354 35
直播预告|Kafka+Flink双引擎实战:手把手带你搭建分布式实时分析平台!
|
5月前
|
消息中间件 运维 Kafka
直播预告|Kafka+Flink 双引擎实战:手把手带你搭建分布式实时分析平台!
直播预告|Kafka+Flink 双引擎实战:手把手带你搭建分布式实时分析平台!
177 11
|
29天前
|
机器学习/深度学习 算法 安全
新型电力系统下多分布式电源接入配电网承载力评估方法研究(Matlab代码实现)
新型电力系统下多分布式电源接入配电网承载力评估方法研究(Matlab代码实现)
|
6月前
|
机器学习/深度学习 人工智能 自然语言处理
ICLR 2025 | EDiT:一种基于 Local SGD 策略的大模型高效分布式训练方法
蚂蚁 AI Infra 团队在深度学习最核心之一的训练框架方向上持续投入与创新,实现了提升资源利用率、加速训练、提升训练稳定性等目标。我们提出的 EDiT 方法,即为其中一项工作。
|
28天前
|
算法 Python
【EI复现】考虑网络动态重构的分布式电源选址定容优化方法(Matlab代码实现)
【EI复现】考虑网络动态重构的分布式电源选址定容优化方法(Matlab代码实现)
|
11月前
|
消息中间件 Java Kafka
在Java中实现分布式事务的常用框架和方法
总之,选择合适的分布式事务框架和方法需要综合考虑业务需求、性能、复杂度等因素。不同的框架和方法都有其特点和适用场景,需要根据具体情况进行评估和选择。同时,随着技术的不断发展,分布式事务的解决方案也在不断更新和完善,以更好地满足业务的需求。你还可以进一步深入研究和了解这些框架和方法,以便在实际应用中更好地实现分布式事务管理。
[go 面试] 雪花算法与分布式ID生成
[go 面试] 雪花算法与分布式ID生成
|
7月前
|
开发框架
osharp集成Yitter.IdGenerator并实现分布式ID
本文介绍了在 osharp 框架中集成 Yitter.IdGenerator 实现分布式 ID 的方法。osharp 是一个基于 .NET Core 的快速开发框架,而 Yitter.IdGenerator 是一种高效的分布式 ID 生成器。通过实现 `IKeyGenerator&lt;long&gt;` 接口并创建 `YitterSnowKeyGenerator` 类,结合 `YitterIdGeneratorPack` 模块化配置,实现了分布式环境下唯一 ID 的生成。
124 0
|
11月前
|
存储 缓存 监控
解决分布式系统演进过程中数据一致性问题的方法
【10月更文挑战第24天】解决分布式系统演进过程中数据一致性问题是一个复杂而又重要的任务。需要综合运用多种方法和技术,根据具体的系统需求和场景,选择合适的解决方案。同时,不断地进行优化和改进,以适应不断变化的分布式系统环境。
531 47

热门文章

最新文章