跳转至

UID生成

UID的生成是软件开发中的一个常见需求,其基本原理是将时间,机器标识,进程标识,输入数据,以及随机数等信息进行混合处理,生成一个在空间和时间上都(或大概率)唯一的标识符。

本质上讲,UID是用来唯一标识一个对象(及其环境)(比如一个请求、一个用户等)的标识符,从实用性的角度考虑,UID一定比原对象短,因此为对象生成UID的过程实际上是一个压缩过程,它将原对象代表的更大的空间(由于现实约束,真实数据只会分布在这个空间的某一区域中)映射到一个UID代表的较小空间中。为了减少UID的长度,我们就必须为原空间假设一个更加严格的约束条件,如每秒生成的对象数,对象的生成时间范围(未来5年内),对象对应的硬件随机数不重复等,当然,如果出现不满足这些约束条件的对象,这个映射就会导致冲突。

下面是几种常见的UID生成方法:

UUID (Universally Unique Identifier):

  1. Version 1: 基于时间戳和MAC地址(或者随机数)

    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                             |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    
    time_low:
    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地址不可用,则使用随机数。

  2. Version 4: 基于随机数

    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                            |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    
    random_a:
    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位布局,可以用随机数据填充。

  3. Version 7: 基于时间戳和随机数
    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                             |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    
    unix_ts_ms:
    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        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
timestamp:
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,且长度可控。缺点是哈希算法可能存在碰撞风险,且完全无序。