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

本文探讨了分布式系统中为何需要分布式ID,业务对ID的全球唯一性、递增性和安全性要求,介绍了UUID、数据库自增、号段模式、Redis、雪花算法、百度Uidgenerator、美团Leaf和滴滴TinyID等生成方案的优缺点,并对比了它们在实际场景中的适用性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、为什么需要分布式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 UUID

UUID (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,如图进行设置:
在这里插入图片描述

这种方案看起来是可行的,但是如果要扩容,步长step等要重新设置,假如只有一台机器,步长就是1,比如1,2,3,4,5,6,这时候如果要进行扩容,就要重新设置,机器2可以挑一个偶数的数字,这个数字在扩容时间内,数据库自增要达不到这个数的,然后步长就是2,机器1要重新设置step为2,然后还是以一个奇数开始进行自增。这个过程看起来不是很杂,但是,如果机器很多的话,那就要花很多时间去维护重新设置

这种实现的缺陷:

  • 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`)
) 

  • biz_type :不同业务类型
  • max_id :当前最大的id
  • step :代表号段的步长
  • version :版本号,就像MVCC一样,可以理解为乐观锁

等ID都用了,再去数据库获取,然后更改最大值

update id_generator set max_id = #{max_id+step}, version = version + 1 where version = # {version} and biz_type = XXX

  • 优点:有比较成熟的方案,像百度Uidgenerator,美团Leaf
  • 缺点:依赖于数据库实现
3.4、 Redis实现

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

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

优缺点:

  • 优点:Redis性能相对比较好,又可以保证唯一性和有序性
  • 缺点:需要依赖Redis来实现,系统需要引进Redis组件
3.4、 雪花算法(SnowFlake)

Snowflake,雪花算法是由Twitter开源的分布式ID生成算法,以划分命名空间的方式将
64-bit位分割成多个部分,每个部分代表不同的含义,64位,在java中Long类型是64位的,所以java程序中一般使用Long类型存储
在这里插入图片描述

  • 第一部分:第一位占用1bit,始终是0,是一个符号位,不使用

  • 第二部分:第2位开始的41位是时间戳。41-bit位可表示241个数,每个数代表毫秒,那么雪花算法可用的时间年限是(241)/(1000606024365)=69 年的时间

  • 第三部分:10-bit位可表示机器数,即2^10 = 1024台机器。通常不会部署这么多台机器

  • 第四部分:12-bit位是自增序列,可表示2^12 = 4096个数。觉得一毫秒个数不够用也可以调大点

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

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

3.5、 百度Uidgenerator

百度的UidGenerator是百度开源基于Java语言实现的唯一ID生成器,是在雪花算法 snowflake 的基础上做了一些改进。
引用官网的解释:

UidGenerator是Java实现的, 基于Snowflake算法的唯一ID生成器。UidGenerator以组件形式工作在应用项目中, 支持自定义workerId位数和初始化策略, 从而适用于docker等虚拟化环境下实例自动重启、漂移等场景。 在实现上, UidGenerator通过借用未来时间来解决sequence天然存在的并发限制; 采用RingBuffer来缓存已生成的UID, 并行化UID的生产和消费, 同时对CacheLine补齐,避免了由RingBuffer带来的硬件级「伪共享」问题. 最终单机QPS可达600万。
在这里插入图片描述
Snowflake算法描述:指定机器 & 同一时刻 & 某一并发序列,是唯一的。据此可生成一个64 bits的唯一ID(long)。默认采用上图字节分配方式:

  • sign(1bit):固定1bit符号标识,即生成的UID为正数。
  • delta seconds (28 bits):当前时间,相对于时间基点"2016-05-20"的增量值,单位:秒,最多可支持约8.7年
  • worker id (22 bits):机器id,最多可支持约420w次机器启动。内置实现为在启动时由数据库分配,默认分配策略为用后即弃,后续可提供复用策略。
  • sequence (13 bits):每秒下的并发序列,13 bits可支持每秒8192个并发。

详细的,可以参考官网解释,链接:https://github.com/baidu/uid-generator/blob/master/README.zh_cn.md

3.6、 美团Leaf

Leaf这个名字是来自德国哲学家、数学家莱布尼茨的一句话: >There are no two
identical leaves in the world > “世界上没有两片相同的树叶”

Leaf 提供两种生成的ID的方式:号段模式(Leaf-segment)和snowflake模式(Leaf-snowflake)。你可以同时开启两种方式,也可以指定开启某种方式,默认两种方式为关闭状态。

  • Leaf­segment数据库方案
    其实就是前面介绍的号段模式的改进,可以引用美团技术博客的介绍:

第一种Leaf-segment方案,在使用数据库的方案上,做了如下改变: - 原方案每次获取ID都得读写一次数据库,造成数据库压力大。改为利用proxy server批量获取,每次获取一个segment(step决定大小)号段的值。用完之后再去数据库获取新的号段,可以大大的减轻数据库的压力。 - 各个业务不同的发号需求用biz_tag字段来区分,每个biz-tag的ID获取相互隔离,互不影响。如果以后有性能需求需要对数据库扩容,不需要上述描述的复杂的扩容操作,只需要对biz_tag分库分表就行

表结构设计:

>+-------------+--------------+------+-----+-------------------+-----------------------------+
| Field       | Type         | Null | Key | Default           | Extra                       |
+-------------+--------------+------+-----+-------------------+-----------------------------+
| biz_tag     | varchar(128) | NO   | PRI |                   |                             |
| max_id      | bigint(20)   | NO   |     | 1                 |                             |
| step        | int(11)      | NO   |     | NULL              |                             |
| desc        | varchar(256) | YES  |     | NULL              |                             |
| update_time | timestamp    | NO   |     | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
+-------------+--------------+------+-----+-------------------+-----------------------------+
  • Leaf­snowflake方案
    Leafsnowflake是在雪花算法上改进来的,引用官网技术博客介绍:

Leaf-snowflake方案完全沿用snowflake方案的bit位设计,即是“1+41+10+12”的方式组装ID号。对于workerID的分配,当服务集群数量较小的情况下,完全可以手动配置。Leaf服务规模较大,动手配置成本太高。所以使用Zookeeper持久顺序节点的特性自动对snowflake节点配置wokerID。Leaf-snowflake是按照下面几个步骤启动的:

  • 启动Leaf-snowflake服务,连接Zookeeper,在leaf_forever父节点下检查自己是否已经注册过(是否有该顺序子节点)。
  • 如果有注册过直接取回自己的workerID(zk顺序节点生成的int类型ID号),启动服务。
  • 如果没有注册过,就在该父节点下面创建一个持久顺序节点,创建成功后取回顺序号当做自己的workerID号,启动服务。

在这里插入图片描述
这种方案解决了前面提到的雪花算法的缺陷,官网没解释,不过Leaf­snowflake对其进行改进,官网的流程图
在这里插入图片描述
详细介绍请看官网:https://tech.meituan.com/2017/04/21/mt-leaf.html

3.7、 滴滴TinyID

Tinyid是用Java开发的一款分布式id生成系统,基于数据库号段算法实现。Tinyid扩展了leaf-segment算法,支持了多数据库和tinyid-client

Tinyid也是基于号段算法实现,系统实现图如下:
在这里插入图片描述

  • 优点:方便集成,有成熟的方案和解决实现
  • 缺点:依赖 DB的稳定性,需要采用集群主从备份的方式提高 DB的可用性
    滴滴TinyID wiki:https://github.com/didi/tinyid/wiki
<think>嗯,用户想了解分布式ID生成的几种方案及其实现方式。首先,我需要回忆一下常见的分布式ID生成方法,比如UUID数据库自增、Snowflake算法、Redis生成ID、Leaf算法等。然后,每个方案的优缺点和具体实现细节需要整理清楚。 首先,用户可能是在设计分布式系统时需要生成唯一ID,所以需要了解各种方案的适用场景。比如,UUID虽然简单,但无序且较长,可能影响数据库性能。Snowflake算法比较流行,但需要注意机器位分配和时钟回拨的问题。Leaf算法作为改进版,支持号段和Snowflake模式,可能更灵活。 接下来,要确保回答结构清晰,分点说明每个方案,包括原理、实现方式和优缺点。需要注意使用中文,并且数学表达式用$...$格式,比如数据库的自增步长设置,可能需要用公式表示,比如$$step = n$$,其中n是节点数。但例子中没有复杂的数学公式,可能不需要太多公式,但还是要检查是否符合系统指令。 另外,用户可能还关心高并发下的性能、ID是否有序、可扩展性等问题。比如Snowflake的ID是有序的,而UUID是无序的,这点对数据库索引影响大。Leaf的号段模式可以预先获取号段,减少数据库压力,适合高并发场景。 需要确保每个方案的解释准确,比如Snowflake的时间戳部分多少位,机器ID如何分配,时钟回拨如何处理。可能还需要提到一些变种,比如MongoDB的ObjectId,或者美团Leaf的具体实现细节。 还要注意不要遗漏重要方案,比如Redis生成ID,虽然可能不常用,但也是可行的方法。同时,优缺点对比要明确,帮助用户根据场景选择合适方案。 最后,总结部分要简明扼要,指导用户根据具体需求选择。比如系统规模、是否需要有序ID、时钟同步问题等。可能用户没有明确这些需求,但总结部分可以帮助他们做决定。 需要检查是否符合LaTeX语法,比如独立公式用$$...$$,行内用$...$。但当前内容中可能涉及的公式较少,比如步长设置或时间戳计算,需要正确使用格式。例如,Snowflake的结构可以表示为$$ID = 时间戳 \ll (机器位 + 序列位) | 机器ID \ll 序列位 | 序列号$$,但可能更清晰的是分部分说明,而不是复杂公式。 总之,需要组织信息,确保准确性和结构清晰,同时遵循格式要求。</think>分布式ID生成分布式系统中的常见需求,主要解决全局唯一性、有序性、高性能等问题。以下是几种主流方案及其实现方式: --- ### **1. UUID** #### 原理与实现 - **核心思路**:基于随机数或时间戳生成128位唯一字符串,如 `550e8400-e29b-41d4-a716-446655440000`。 - **实现方式**: ```java UUID uuid = UUID.randomUUID(); ``` #### 优缺点 - **优点**:简单,无需中心化协调。 - **缺点**: - 无序性影响数据库索引性能; - 128位过长,存储浪费。 --- ### **2. 数据库自增ID** #### 原理与实现 - **单机模式**:通过 `AUTO_INCREMENT` 实现,但无法扩展。 - **分布式改进**: 1. **分步自增**:每个数据库实例设置不同初始值和步长,例如: $$初始值 = 1, 步长 = 实例数$$ 2. **号段模式**:批量申请ID区间(如一次取1000个ID),减少数据库访问压力。 #### 优缺点 - **优点**:ID有序,易扩展。 - **缺点**:依赖数据库,高并发下有性能瓶颈。 --- ### **3. Snowflake算法(Twitter)** #### 原理与实现 - **ID结构**(64位): - **时间戳(41位)**:毫秒级时间,可用约69年; - **机器ID(10位)**:手动分配或通过协调服务(如ZooKeeper)分配; - **序列号(12位)**:同一毫秒内的自增数。 - **生成逻辑**: ```java long id = (timestamp << 22) | (machineId << 12) | sequence; ``` #### 优缺点 - **优点**:高性能、趋势递增、无中心化依赖。 - **缺点**: - 时钟回拨需特殊处理(如等待或报错); - 机器ID需手动管理。 --- ### **4. Redis生成ID** #### 原理与实现 - **核心思路**:利用Redis的原子操作 `INCR` 或 `INCRBY` 生成自增ID。 - **实现方式**: ```redis INCR global_id_counter # 返回1,2,3... ``` #### 优缺点 - **优点**:性能高于数据库。 - **缺点**:需维护Redis集群,持久化策略影响可靠性。 --- ### **5. Leaf(美团开源)** #### 原理与实现 - **两种模式**: 1. **号段模式**: - 从数据库批量获取ID区间(如1-1000),加载到内存分配; - 通过双Buffer预加载避免分配延迟。 2. **Snowflake模式**: - 改进时钟回拨问题,依赖ZooKeeper分配机器ID。 #### 优缺点 - **优点**:高可用、支持高并发。 - **缺点**:系统复杂度较高。 --- ### **总结与选型建议** | 方案 | 适用场景 | 关键考虑点 | |--------------|------------------------------|------------------------------| | UUID | 简单场景,容忍无序性 | 存储和索引性能影响 | | 数据库自增 | 中小规模系统 | 数据库性能和扩展性 | | Snowflake | 高并发,需有序ID | 时钟同步和机器ID管理 | | Redis | 已有Redis基础设施 | 集群稳定性和持久化 | | Leaf | 大规模分布式系统 | 部署和维护成本 | **最终选择需权衡**:ID长度、有序性要求、系统规模、基础设施复杂度。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Nicky.Ma

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值