分布式ID生成方案
一、什么是分布式ID
分布式ID(Distributed ID)是指在分布式系统环境下,由多个服务节点协同或者独立生成的、能够确保全局唯一性的数据标识符。
为什么需要它?—— 传统单机ID的局限性
在单机数据库时代,我们通常使用数据库的自增主键(Auto Increment Primary Key) 来为数据赋予一个唯一的ID。这种方式简单可靠。
但是,在分布式系统(如微服务架构)中,问题变得复杂:
- 分库分表:数据被水平拆分到多个数据库或表中。如果每个库/表都使用自己的自增ID,很快就会产生重复的ID,无法保证全局唯一。
- 性能瓶颈:如果所有ID都从一个中央数据库的自增序列获取,这个数据库就会成为系统的单点瓶颈和潜在故障点,无法满足高并发场景。
- 安全性与连续性:直接暴露的自增ID很容易被猜出业务量(例如,通过ID大小推测订单数量),存在安全隐患。同时,自增ID通常是连续的,在某些场景下不希望被猜测。
分布式ID的核心需求
一个理想的分布式ID生成方案,应该尽可能满足以下要求:
- 全局唯一:这是最基本的要求,绝不能出现重复的ID。
- 高性能高可用:生成服务必须能应对高并发请求,且需要高可用,不能有单点故障。
- 趋势递增:生成的ID最好是随时间趋势递增的(但不必严格连续)。这对于使用ID作为索引的数据库(如MySQL InnoDB)非常友好,能提高写入性能。
- 安全:ID中不应包含可能泄露业务信息的敏感内容(如订单号直接对应日期)。
- 低延迟:生成速度要快,延迟要低。
- 易于使用:接入和使用方式应该尽可能简单。
二、有哪些解决方案
分布式ID的解决方案非常多,可以根据其核心思想分为以下几大类:
1. 基于数据库方案
a) 数据库号段模式(Segment)
这是对传统数据库自增的一种优化,也是许多开源框架(如Leaf)的核心思想。
- 原理:不再是每次取一个ID,而是由一台数据库服务器预先分配一个号段(比如1~1000)。业务服务将整个号段加载到内存中,然后在本地方派发ID。当号段用尽后,再向数据库申请新的号段。
- 优点:大大减少了数据库的访问次数,性能极高。可用性可以通过数据库主从来保证。
- 缺点:依然依赖数据库,数据库宕机会导致整个系统不可用(除非有备用节点)。
b) 数据库自增ID + 多实例模式
- 原理:设置多台数据库实例,每台实例的起始值和步长不同。
- 实例1:
auto_increment_offset = 1,auto_increment_increment = 2-> ID: 1, 3, 5, 7… - 实例2:
auto_increment_offset = 2,auto_increment_increment = 2-> ID: 2, 4, 6, 8…
- 实例1:
- 优点:解决了单点性能问题。
- 缺点:非常不推荐! 扩展性极差,一旦需要新增实例,重新设置步长和偏移量会非常麻烦,容易出错。
2. UUID
- 原理:生成一个36位的字符串(如
550e8400-e29b-41d4-a716-446655440000),标准格式包含32个16进制数字,分为五段。其本身可以通过MAC地址、时间戳、随机数等保证全局唯一。 - 优点:生成简单,本地生成无网络消耗,绝对唯一。
- 缺点:
- 作为数据库主键性能差:字符串过长,且无序,会导致B+树索引频繁分裂,严重影响写入性能。
- 不满足趋势递增。
- 无可读性。
3. Redis方案
- 原理:利用Redis的单线程原子操作
INCR或INCRBY来生成递增的ID。 - 优点:性能优于数据库,且天然递增。
- 缺点:需要引入和维护Redis。存在持久化问题:如果Redis宕机且重启后数据丢失,可能会生成重复ID(虽然可以通过AOF持久化解决,但会影响性能)。
4. Snowflake(雪花算法)及其变种
这是目前业界最流行、应用最广泛的方案。Twitter开源(但未维护)的分布式ID生成算法。
- 原理:生成一个64位的Long型数字ID,其结构如下:
- 1位符号位:固定为0。
- 41位时间戳(毫秒):可以持续使用约69年。
- 10位工作机器ID(Datacenter ID + Worker ID):最多支持1024个节点。
- 12位序列号:每毫秒内最多生成4096个ID。
- 工作流程:在同一毫秒内,如果某个节点收到请求,它会增加其序列号。如果序列号用完,就阻塞到下一毫秒再继续生成。
- 优点:
- 高性能:本地生成,无需网络开销。
- 趋势递增:ID是数值型且随时间增大。
- 灵活:可根据业务调整各部分的位数。
- 缺点:
- 时钟回拨问题:如果机器时钟发生回退(例如被NTP同步纠正),可能会导致生成重复ID。这是最大的挑战,需要在算法中解决(如等待时钟追上来、记录最后生成时间戳等)。
- 需要维护工作机器ID:需要保证每个节点的Worker ID不重复,通常需要通过ZK/Etcd或数据库来分配。
变种:各大公司对Snowflake的改进,如百度的UidGenerator、美团的Leaf(支持号段模式和snowflake模式)。
5. 开源中间件
直接使用成熟的开源解决方案,省去自研的麻烦。
- Leaf:美团开源的分布式ID生成系统,集成了号段模式和Snowflake模式,提供了高可用和高性能的保障。
- Tinyid:滴滴开源,基于Leaf的号段模式做的扩展。
6. 借助第三方服务
- 原理:使用如ZooKeeper、Etcd等协调服务的强一致性特性来生成唯一ID。
- 优点:强一致性保证。
- 缺点:性能较差,因为每次生成ID都需要与集群进行交互,延迟高,不适合高并发场景。
三、方案对比总结
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 数据库号段 | 高性能,趋势递增 | 依赖DB,有宕机风险 | 大部分分布式业务场景,如Leaf |
| UUID | 简单,无网络消耗,全球唯一 | 无序,字符串长,性能差 | 不需要DB主键或对性能要求不高的场景 |
| Redis | 性能优于DB,递增 | 需维护Redis,有持久化问题 | 已有Redis且可容忍小概率重复的场景 |
| Snowflake | 高性能,趋势递增,本地生成 | 有时钟回拨问题,需管理WorkerID | 大规模、高并发系统,如电商、金融 |
| 开源中间件 | 功能完善,开箱即用 | 需要引入外部依赖 | 追求稳定、不想自研的公司 |
如何选择
- 中小型系统:推荐使用数据库号段模式或接入Leaf这类开源中间件,实现简单,足够可靠。
- 大型高并发系统:推荐使用Snowflake或其变种(如美团Leaf-snowflake模式),但必须解决好时钟回拨和WorkerID分配问题。
- 简单临时方案:如果对性能和存储要求不高,可以使用UUID。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 技术之路!
评论


