分布式 ID 生成
本文最后更新于 2025年8月14日 11:19
分布式 ID 应当满足的要求
- 全局唯一:ID 的全局唯一性肯定是首先要满足的!
- 高性能:分布式 ID 的生成速度要快,对本地资源消耗要小。
- 高可用:生成分布式 ID 的服务要保证可用性无限接近于 100%。
- 方便易用:拿来即用,使用方便,快速接入!
除此之外,我们还可以对分布式 ID 提出一些更高的要求:
- 安全:ID 中不包含敏感信息。
- 有序递增:如果要把 ID 存放在数据库的话,ID 的有序性可以提升数据库写入速度。并且,很多时候 ,我们还很有可能会直接通过 ID 来进行排序。
- 有具体的业务含义:生成的 ID 如果能有具体的业务含义,可以让定位问题以及开发更透明化(通过 ID 就能确定是哪个业务)。
- 独立部署:这样生成 ID 的服务可以和业务相关的服务解耦,不过同样带来了网络调用消耗增加的问题。
分布式 ID 解决方案
目前市面上已经有很多成熟的分布式 ID 解决方案,简单分析一下:
数据库
- 数据库主键自增:优点是简单、ID有序递增;缺点是并发受限于数据库的性能、ID没有业务意义、存在数据库单点问题、安全问题(暴露信息隐私,如订单号)。
- 数据库号段:批量生成后存储到内存中,优缺点基本同上,不过由于基于内存实现,可以支持更高的并发,每次获取一个分布式ID也不用查询DB。
- NoSQL(Redis):优点是ID有序递增,性能好;缺点基本同上。
算法
- UUID:优点是生成速度快,使用方便(JDK内置);缺点是存储空间占用大(128位),不安全(基于MAC地址),无序,无业务含义,机器时间异常可能导致重复生成。
- 雪花算法:优点是生成速度快,ID有序递增,较为灵活;缺点是时间回拨会导致ID重复生成(有很多开源解决方案已经改进了这个问题并提高了生成性能,比如美团的 Leaf、百度的 UidGenerator)
以 TraceID 为例试析分布式 ID 设计方案
TraceID 是分布式系统中用于唯一标识一次请求链路的标识符。它贯穿整个调用链,串联服务间的所有日志和操作,帮助开发者追踪请求的完整路径,定位跨服务的问题。例如,用户下单请求经过网关、订单服务、库存服务时,所有相关日志都会携带相同的TraceID,便于分析延迟、错误和调用关系。通常由入口服务生成,并随请求头传递。
这种场景下,生成的 ID 除了要求唯一之外,还要求生成的效率高、吞吐量大。TraceID 需要具备接入层的服务器实例自主生成的能力,如果每个 Trace 中的 ID 都需要请求公共的 ID 服务生成,会非常浪费网络带宽资源,且会阻塞用户请求向下游传递,响应耗时上升。所以需要服务器实例最好可以自行计算生成 TraceID,避免依赖外部服务。
产生规则:服务器 IP + ID 产生的时间 + 自增序列 + 当前进程号 ,比如:
0ad1348f1403169275002100356696
前 8 位 0ad1348f
即产生 TraceId 的机器的 IP,这是一个十六进制的数字,每两位代表 IP 中的一段,我们把这个数字,按每两位转成 10 进制即可得到常见的 IP 地址表示方式 10.209.52.143
,也可以根据这个规律来查找到请求经过的第一个服务器。
后面的 13 位 1403169275002
是产生 TraceId 的时间。之后的 4 位 1003
是一个自增的序列,范围从 1000 到 9000。最后的 5 位 56696
是当前的进程 ID,为了防止单机多进程出现 TraceID 冲突的情况,所以在 TraceID 末尾添加了当前的进程 ID。