Skip to content

UUID 算法

UUIDv7 (时间顺序排序)

UUIDv7 与广泛使用的 UUIDv4 等同类产品一样,都是 128 位唯一标识符。但与 v4 不同的是,UUIDv7 可进行时间排序,精度为 1 毫秒。通过结合时间戳和随机部分,UUIDv7 成为数据库(包括分布式数据库)中记录标识符的绝佳选择。

结构

128 位数值由几个部分组成:

  • timestamp (48 bits) 是以毫秒为单位的 Unix 时间戳。
  • ver (4 bits) 是 UUID 版本 (7)。
  • rand_a (12 bits) 是随机生成的。
  • var* (2 bits) 等于 10
  • rand_b (62 bits) 是随机生成的。

TIP

* 在字符串表示法中,每个符号编码 4 位十六进制数,因此示例中的 a 是 1010,其中前两位是固定变量(10),后两位是随机数。 因此得到的十六进制数可以是 8 (1000)、9 (1001)、a (1010) 或 b (1011)。

0190163d-8694-739b-aea5-966c26f8ad91
└─timestamp─┘ │└─┤ │└───rand_b─────┘
             ver │var
              rand_a

实现

java
import java.nio.ByteBuffer;
import java.security.SecureRandom;
import java.util.UUID;

public class UUIDv7 {
    private static final SecureRandom random = new SecureRandom();

    public static UUID randomUUID() {
        byte[] value = randomBytes();
        ByteBuffer buf = ByteBuffer.wrap(value);
        long high = buf.getLong();
        long low = buf.getLong();
        return new UUID(high, low);
    }

    public static byte[] randomBytes() {
        // random bytes
        byte[] value = new byte[16];
        random.nextBytes(value);

        // current timestamp in ms
        ByteBuffer timestamp = ByteBuffer.allocate(Long.BYTES);
        timestamp.putLong(System.currentTimeMillis());

        // timestamp
        System.arraycopy(timestamp.array(), 2, value, 0, 6);

        // version and variant
        value[6] = (byte) ((value[6] & 0x0F) | 0x70);
        value[8] = (byte) ((value[8] & 0x3F) | 0x80);

        return value;
    }

    public static void main(String[] args) {
        var uuid = UUIDv7.randomUUID();
        System.out.println(uuid);
    }
}
python
import os
import time

def uuidv7():
    # random bytes
    value = bytearray(os.urandom(16))

    # current timestamp in ms
    timestamp = int(time.time() * 1000)

    # timestamp
    value[0] = (timestamp >> 40) & 0xFF
    value[1] = (timestamp >> 32) & 0xFF
    value[2] = (timestamp >> 24) & 0xFF
    value[3] = (timestamp >> 16) & 0xFF
    value[4] = (timestamp >> 8) & 0xFF
    value[5] = timestamp & 0xFF

    # version and variant
    value[6] = (value[6] & 0x0F) | 0x70
    value[8] = (value[8] & 0x3F) | 0x80

    return value

if __name__ == "__main__":
    uuid_val = uuidv7()
    print(''.join(f'{byte:02x}' for byte in uuid_val))
C++
#include <array>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <random>

std::array<uint8_t, 16> uuidv7() {
    // random bytes
    std::random_device rd;
    std::array<uint8_t, 16> random_bytes;
    std::generate(random_bytes.begin(), random_bytes.end(), std::ref(rd));
    std::array<uint8_t, 16> value;
    std::copy(random_bytes.begin(), random_bytes.end(), value.begin());

    // current timestamp in ms
    auto now = std::chrono::system_clock::now();
    auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(
        now.time_since_epoch()
    ).count();

    // timestamp
    value[0] = (millis >> 40) & 0xFF;
    value[1] = (millis >> 32) & 0xFF;
    value[2] = (millis >> 24) & 0xFF;
    value[3] = (millis >> 16) & 0xFF;
    value[4] = (millis >> 8) & 0xFF;
    value[5] = millis & 0xFF;

    // version and variant
    value[6] = (value[6] & 0x0F) | 0x70;
    value[8] = (value[8] & 0x3F) | 0x80;

    return value;
}

int main() {
    auto uuid_val = uuidv7();
    for (const auto& byte : uuid_val) {
        printf("%02x", byte);
    }
    printf("\n");
    return 0;
}
JavaScript
function uuidv7() {
    // random bytes
    const value = new Uint8Array(16);
    crypto.getRandomValues(value);

    // current timestamp in ms
    const timestamp = BigInt(Date.now());

    // timestamp
    value[0] = Number((timestamp >> 40n) & 0xffn);
    value[1] = Number((timestamp >> 32n) & 0xffn);
    value[2] = Number((timestamp >> 24n) & 0xffn);
    value[3] = Number((timestamp >> 16n) & 0xffn);
    value[4] = Number((timestamp >> 8n) & 0xffn);
    value[5] = Number(timestamp & 0xffn);

    // version and variant
    value[6] = (value[6] & 0x0f) | 0x70;
    value[8] = (value[8] & 0x3f) | 0x80;

    return value;
}

const uuidVal = uuidv7();
const uuidStr = Array.from(uuidVal)
    .map((b) => b.toString(16).padStart(2, "0"))
    .join("");
console.log(uuidStr);

UUIDv4 (完全随机或伪随机)

UUID(Universally Unique Identifier)是一个由128位构成的标识符,通常以32个十六进制数字表示,并通过破折号分为五组,形成8-4-4-4-12的格式。例如," 123e4567-e89b-12d3-a456-426655440000"。UUID v4的每个十六进制字符可取值范围为0至f。在数据库中,UUID v4可以以字符串形式或直接以16字节的二进制形式存储。

UUID v4通过完全随机或伪随机数生成器生成,确保了其高度的唯一性。据估计,生成的UUID v4样本具有3.26*10¹⁶的基数,重复概率低于0.01%。

对比

粤ICP备20009776号