前言
在单体应用中,一般直接用表的自增ID或者UUID作为唯一标识。业务体量增大在分库分表之后,如何生成一个全局唯一的ID,就是一个关键的问题。
通常情况下,对于分布式ID来说,我们一般希望他具有以下几个特点:
● 全局唯一:必须保证全局唯一性,这个是最基本的要求。
● 高性能&高可用:需要保证ID的生成是稳定且高效的。
● 递增:根据不同的业务情况,有的会要求生成的ID呈递增趋势,也有的要求必须单调递增(后一个ID必须比前一个大),也有的没有严格要求。
分布式ID方案
具体的分布式ID有以下这些方案:
方案 | 全局唯一 | 高性能 / 高可用 | 递增性 / 有序性 | 依赖组件 | 适用场景 |
---|---|---|---|---|---|
UUID | ✅ | 本地生成,高性能 | 无序 | 无 | 无需有序性的通用场景 |
数据库自增 | ✅ | 单点瓶颈,低可用 | 严格递增 | 单库单表 | 低并发、强依赖递增的场景 |
号段模式 | ✅ | 减少数据库访问 | 趋势递增(非严格) | 数据库 | 中高并发、可接受 ID 不连续场景 |
Redis | ✅ | 集群支持高可用 | 严格递增 | Redis 集群 | 高并发、需严格递增的场景 |
雪花算法 | ✅ | 单机高性能 | 严格递增 | 时钟同步 | 高并发、需有序性的场景 |
UUID
UUID(Universally Unique Identifier)全局唯一标识符,是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。
标准的UUID格式为:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12),共32个字符,通常由以下几部分的组合而成:当前日期和时间,时钟序列,全局唯一的IEEE机器识别号
优缺点
-
优点: UUID的优点就是他的性能比较高,不依赖网络,本地就可以生成,使用起来也比较简单。
-
缺点:长度过长和没有任何含义。一旦使用它作为全局唯一标识,就意味着在日后的问题排查和开发调试过程中会遇到很大的困难。
不适合范围查询、不方便展示以及查询效率低等问题。
数据库自增
分布式ID也可以使用数据库的自增ID,但是这种实现中就要求一定是一个单库单表才能保证ID自增且不重复.
优缺点
- 优点:使用简单,容易维护,查询效率高支持范围查询。
- 缺点:一旦这个数据库挂了,那整个分布式ID的生成服务就挂了。而且还存在一个性能问题,如果高并发访问数据库的话,就会带来阻塞问题。
号段模式
号段模式是在数据库的基础上,为了解决性能问题而产生的一种方案。他的意思就是每次去数据库中取ID的时候取出来一批,并放在缓存中,然后下一次生成新ID的时候就从缓存中取。这一批用完了再去数据库中拿新的。
而为了防止多个实例之间发生冲突,需要采用号段的方式,即给每个客户端发放的时候按号段分开,如客户端A取的号段是1-1000,客户端B取的是1001-2000,客户端C取的是2001-3000。当客户端A用完之后,再来取的时候取到的是3001-4000。
优缺点
- 优点:在同一个客户端中,生成的ID是顺序递增的。并且不需要频繁的访问数据库,也能提升获取ID的性能。
- 缺点:是没办法保证全局顺序递增,也存在数据库的单点故障问题。
其实很多分库分表的中间件的主键ID的生成,主要采用的也是号段模式,如TDDL Sequence
Redis 实现
基于数据库可以实现,那么基于Redis也是可以的,我们可以依赖Redis的incr命令实现ID的原子性自增。R
优缺点
- 优点:借助集群解决单点故障的问题,性能也比较高。
- 缺点:存在数据丢失的情况,无论是那种持久化机制,都无法完全避免。
雪花算法
雪花算法(Snowflake)是由Twitter研发的一种分布式ID生成算法,它可以生成全局唯一且递增的ID。它的核心思想是将一个64位的ID划分成多个部分,每个部分都有不同的含义,包括时间戳、数据中心标识、机器标识和序列号等。
雪花算法生成的ID由以下几个部分组成:
- 符号位(1bit):固定为 0(保证 ID 为正数)。
- 时间戳(41bit):精确到毫秒级别,41位的时间戳可以容纳的毫秒数是2的41次幂,一年所使用的毫秒数是:365 * 24 * 60 * 60 * 1000,算下来可以使用69年。
- 数据中心标识(5bit):可以用来区分不同的数据中心。
- 机器标识(5bit):可以用来区分不同的机器。
- 序列号(12bit):同一毫秒内每台机器可生成 4096 个 ID。
优缺点
- 优点:性能好、有序,比较灵活各部分位数可根据业务需求调整
- 缺点:时钟回拨问题( 若机器时钟因硬件故障、时区调整或 NTP 同步等原因回退到之前的时间,可能生成重复 ID。);时间戳占用位数与有效期限制;单节点并发上限(12 位序列号允许单节点单毫秒生成 4096 个 ID,若瞬时并发超过该阈值(如秒杀场景),需等待下一毫秒,可能导致请求阻塞或业务延迟。)
第三方工具
百度 Uidgenerator
是百度开源基于 Java 语言实现的唯一 ID 生成器,是在雪花算法 Snowflake 的基础上做了一些改进,如支持自定义位数和生成策略等。
-
优点
- 高性能:支持每秒生成数百万个 ID,单个实例 QPS 能超过 6000000,可满足高并发场景需求。
- 高可用性:支持多节点部署,某个节点宕机不影响系统正常运行。
- 易于使用:提供简单易用的 API,便于集成到现有系统。
- 可定制化:支持自定义机器 ID 和序列号生成方式,能根据实际需求定制。
- 解决时钟回拨问题:通过采用 AtomicLong 类型的时间,并使用 incrementAndGet () 方法获取下一次时间,脱离了对服务器时间的依赖,避免了时钟回拨问题。
-
缺点
- 趋势自增:生成的 ID 是趋势自增的,在某些对 ID 随机性有要求的场景中不适用。
- 依赖 MySQL:依赖 MySQL 做 workerId 分发,使用 MySQL 自增 Id 做 workId,用后即弃,增加了对数据库的依赖。
- 时间范围有限:UidGenerator 的时间部分只有 28 位,默认只能承受 8.5 年。
美团 Leaf
兼具数据库号段模式和雪花算法两种方式,用户可以同时开启两种方式,也可以指定开启某种方式,能够根据不同业务场景灵活切换。
-
优点
- 支持多种模式:支持号段模式和雪花算法两种模式,可根据不同业务场景灵活选择。
- 高性能和高可用:号段模式下,通过一次请求获取一批自增 ID,减少访库次数,降低数据库读写压力;在雪花算法模式下,也能提供较高的性能和可用性。
- 全局唯一:能够保证生成的 ID 在全局范围内是唯一的。
- 趋势递增:无论是号段模式还是雪花算法模式,都能保证生成的 ID 是趋势递增的,适合对 ID 有序性有要求的场景。
-
缺点
- 号段模式依赖数据库:号段模式下强依赖于数据库,如果数据库出现故障,可能会影响 ID 的生成。不过可以通过数据库集群等方式来提高可用性。
- 雪花算法模式依赖时钟:雪花算法模式强依赖机器时钟,如果发生时钟回拨,可能导致发号重复或服务不可用。
滴滴 Tinyid
是滴滴用 Java 开发的一款分布式 ID 生成系统,基于数据库号段算法实现,扩展了 leaf - segment 算法,支持了多 db(master),同时提供了 java - client(SDK)使 ID 生成本地化,获得了更好的性能与可用性
-
优点
- 全局唯一:能够生成全局唯一的 long 型 ID。
- 性能较高:通过将号段加载到内存中,ID 为本地生成,号段长度越长,QPS 越高,最高可达 1000w+。
- 可用性较好:支持多 db 配置,只要有一台 server 存活,理论上服务就可用;即使 server 全挂,由于 client 有缓存,也能继续使用一段时间。
- 功能丰富:支持批量获取 ID,还支持生成特定序列的 ID,如 1,3,5,7,9 等。
-
缺点
- 不保证严格递增:生成的 ID 是趋势递增,但不保证下一个 ID 一定比上一个大,不适合对 ID 单调性要求严格的场景。
- ID 连续性问题:由于在 server 或 client 重启时,内存中预加载的号段会作废,下次请求会获得新号段,导致 ID 不连续,会浪费一部分 ID,不适合对 ID 连续性要求高的场景。
- 不适用特定业务:生成的 ID 大部分是连续的,容易被扫库或测算出订单量等信息,不适合类似订单 ID 等对信息安全性要求高的业务。
总结
分布式 ID 生成的核心是平衡 唯一性、性能、有序性 与 系统依赖 。 UUID 和 数据库自增 是入门方案,适合简单场景; 雪花算法及其变种(Uidgenerator、Leaf) 是中高频选择,兼顾性能与有序性; Tinyid 和 Redis 则面向极致性能需求,但需接受 ID 不连续或缓存依赖。
实际选型时,需结合业务规模、技术栈和未来扩展规划,对系统性能要求或者扩展性高的可优先选择成熟开源方案(如 Leaf、Tinyid),避免重复造轮子。