UID生成
UID的生成是软件开发中的一个常见需求,其基本原理是将时间,机器标识,进程标识,输入数据,以及随机数等信息进行混合处理,生成一个在空间和时间上都(或大概率)唯一的标识符。
本质上讲,UID是用来唯一标识一个对象(及其环境)(比如一个请求、一个用户等)的标识符,从实用性的角度考虑,UID一定比原对象短,因此为对象生成UID的过程实际上是一个压缩过程,它将原对象代表的更大的空间(由于现实约束,真实数据只会分布在这个空间的某一区域中)映射到一个UID代表的较小空间中。为了减少UID的长度,我们就必须为原空间假设一个更加严格的约束条件,如每秒生成的对象数,对象的生成时间范围(未来5年内),对象对应的硬件随机数不重复等,当然,如果出现不满足这些约束条件的对象,这个映射就会导致冲突。
下面是几种常见的UID生成方法:
UUID (Universally Unique Identifier):¶
-
Version 1: 基于时间戳和MAC地址(或者随机数)
time_low:0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | time_low | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | time_mid | ver | time_high | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |var| clock_seq | node | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | node | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
0-31 bits: 表示时间戳的低32位。
time_mid:
32-47 bits: 表示时间戳的中间16位。
ver:
48-51 bits: 版本号,固定为0b0001 (1)。
time_high:
52-63 bits: 表示时间戳的高12位。
var:
64-65 bits: 变体字段,固定为0b10。
clock_seq:
66-79 bits: 包含时钟序列的14位。
node:
80-127 bits: 48位空间唯一标识符。 通常是主机的MAC地址,如果MAC地址不可用,则使用随机数。 -
Version 4: 基于随机数
random_a:0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | random_a | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | random_a | ver | random_b | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |var| random_c | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | random_c | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
0-47 bits: 可以用随机数据填充的前48位布局。
ver:
48-51 bits: 版本字段,固定为0b0100 (4)。
random_b:
52-63 bits: 可以用随机数据填充的另外12位布局。
var:
64-65 bits: 变体字段,固定为0b10。
random_c: 66-127 bits: 紧跟在var字段之后的最后62位布局,可以用随机数据填充。 - Version 7: 基于时间戳和随机数
unix_ts_ms:
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | unix_ts_ms | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | unix_ts_ms | ver | rand_a | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |var| rand_b | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | rand_b | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
0-47 bits: 大端序的48位无符号整数,表示Unix纪元时间戳(毫秒)。
ver: 48-51 bits: 版本字段,固定为0b0111 (7)。
rand_a: 52-63 bits: 可以用随机数据填充的12位布局。
var: 64-65 bits: 变体字段,固定为0b10。
rand_b: 66-127 bits: 紧跟在var字段之后的最后62位布局,可以用随机数据填充。
UUID的优点是标准化程度高,几乎所有编程语言和平台都支持UUID的生成和解析,且生成速度快,适合大多数应用场景。缺点是长度较长(128位),在某些对存储空间敏感的应用中可能不适用。其中: - UUIDv1包含MAC地址,存在信息泄露风险。 - UUIDv4完全随机,存在碰撞风险,但概率极低。 - UUIDv7结合时间戳和随机数,兼顾排序和唯一性。
Snowflake:¶
Snowflake是由Twitter开发的一种分布式唯一ID生成算法,生成的ID是64位的整数,通常表示为一个长整型数字。标准的Snowflake ID的结构如下(实际使用中可以根据实际情况改变各个段的长度):
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0| timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp | machine_id | sequence |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1-41 bits: 表示自定义纪元(通常是某个固定时间点)以来的毫秒数。
machine_id:
42-55 bits: 表示机器ID,通常是数据中心ID和机器ID的组合。
sequence:
56-64 bits: 表示同一毫秒内生成的序列号,支持同一毫秒内生成多个ID。
Snowflake的优点是生成的ID是有序的,适合数据库索引,且长度较短(64位),适合高并发场景。缺点是需要依赖时间戳和机器ID,如果时间回拨或机器ID冲突,可能导致ID重复。
自增ID¶
自增ID是最简单的一种UID生成方式,通常由数据库自动生成,每次插入新记录时,数据库会自动为该记录分配一个比当前最大ID值大1的ID。自增ID的优点是简单易用,且生成的ID是有序的,适合数据库索引。缺点是必须依赖一个分发中心(如数据库),在分布式系统中可能成为瓶颈,且容易被猜测,存在安全隐患。
哈希值¶
通过对输入数据(如用户信息、请求参数等)进行哈希计算生成UID。常用的哈希算法有MD5、SHA-1、SHA-256等。哈希值的优点是可以根据输入数据生成唯一的ID,且长度可控。缺点是哈希算法可能存在碰撞风险,且完全无序。