Trình tạo UUID v7
Tạo UUID v7 theo thứ tự thời gian cho khóa chính cơ sở dữ liệu
…
Định dạng
UUID v7 là gì?
UUID v7 là định dạng UUID thế hệ tiếp theo được chuẩn hóa trong RFC 9562 (tháng 5 năm 2024). UUID v7 mã hóa timestamp Unix 48-bit vào các bit có ý nghĩa nhất, tiếp theo là các dấu phiên bản và biến thể, và điền các bit còn lại với dữ liệu ngẫu nhiên an toàn về mặt mật mã.
Vì timestamp chiếm các bit cao, các giá trị UUID v7 được sắp xếp theo trình tự thời gian. Điều này làm cho chúng phù hợp tuyệt vời cho các khóa chính database, nơi UUID ngẫu nhiên (v4) gây ra phân mảnh chỉ mục B-tree.
Tại sao UUID Ngẫu nhiên Phân mảnh Chỉ mục Database
Chỉ mục B-tree — được sử dụng bởi PostgreSQL, MySQL, SQLite, và hầu hết các database khác — giữ các hàng được sắp xếp theo giá trị khóa.
Với UUID v4 (hoàn toàn ngẫu nhiên), mỗi lần chèn mới rơi vào một vị trí về cơ bản ngẫu nhiên trong cây chỉ mục. Điều này buộc database phải đọc và ghi lại các trang chỉ mục nội bộ liên tục.
Bố cục Bit UUID v7
UUID v7 rộng 128 bit, được chia thành sáu trường:
| Bit | Trường | Mục đích |
|---|---|---|
| 48 | unix_ts_ms | Timestamp Unix 48-bit tính bằng mili giây — chiếm toàn bộ nửa cao |
| 4 | ver | Số phiên bản — luôn là 0111 (thập phân 7) |
| 12 | rand_a | 12 bit dữ liệu ngẫu nhiên an toàn về mặt mật mã |
| 2 | var | Dấu biến thể — luôn là 10 (biến thể RFC 4122) |
| 62 | rand_b | 62 bit dữ liệu ngẫu nhiên an toàn về mặt mật mã |
Độ chính xác timestamp là 1 mili giây. Trong cùng một mili giây, các giá trị UUID v7 được sắp xếp theo hậu tố ngẫu nhiên của chúng.
UUID v7 so với UUID v1
UUID v1 và UUID v7 đều nhúng timestamp, nhưng khác nhau đáng kể về thiết kế:
| Tính năng | UUID v7 | UUID v1 |
|---|---|---|
| Epoch / Cơ sở Thời gian | Epoch Unix (1 tháng 1 năm 1970) | Epoch Gregorian (15 tháng 10 năm 1582) |
| Độ chính xác Thời gian | 1 mili giây | 100 nano giây |
| Có thể Sắp xếp | Có — k-sortable theo thiết kế | Không — các trường thời gian bị xáo trộn trong bố cục UUID |
| Quyền riêng tư | Không rò rỉ thông tin máy chủ | Nhúng địa chỉ MAC của máy chủ tạo |
| Hiệu suất Chỉ mục DB | Xuất sắc — chèn tuần tự, phân mảnh tối thiểu | Kém — không tuần tự dù có timestamp |
| Tiêu chuẩn | RFC 9562 (2024) | RFC 4122 (2005) |
| Hỗ trợ Trình duyệt Gốc | Chưa (không có crypto.randomUUID v7) | Không có sẵn gốc |
Đối với bất kỳ dự án mới nào cần UUID theo thứ tự thời gian, hãy ưu tiên UUID v7 hơn UUID v1.
UUID v7 so với ULID
ULID (Universally Unique Lexicographically Sortable Identifier) giải quyết vấn đề tương tự như UUID v7:
- Tuân theo tiêu chuẩn UUID RFC 9562 — tương thích với tất cả công cụ UUID
- Định dạng hex có gạch nối — được nhận dạng toàn cầu
- Hỗ trợ cột UUID database gốc
- 128 bit tổng cộng
- Mã hóa Crockford Base32 — 26 ký tự, nhỏ gọn hơn một chút
- Không phân biệt chữ hoa chữ thường và tránh các ký tự mơ hồ (I, L, O, U)
- Dễ đọc hơn với con người nhìn thoáng qua
- Yêu cầu thư viện — không có hỗ trợ nền tảng gốc
Nếu bạn đã trong hệ sinh thái UUID (cột uuid PostgreSQL, REST API), hãy sử dụng UUID v7. Nếu bắt đầu mới và thích mã hóa thân thiện với con người hơn, ULID là lựa chọn thay thế hợp lý.
Sử dụng UUID v7 trong Database
UUID v7 chưa được hầu hết các database tạo ra nội bộ, nhưng có thể được lưu trong các cột UUID tiêu chuẩn:
uuid. Tiện ích mở rộng pg-uuidv7 thêm hàm phía server uuid_generate_v7().BINARY(16) hoặc CHAR(36). Tạo trong mã ứng dụng.TEXT (36 ký tự) hoặc BLOB (16 byte). Tạo trong mã ứng dụng.Ví dụ Mã
UUID v7 chưa có sẵn qua crypto.randomUUID(). Sử dụng thư viện như uuidv7 (npm) cho đến khi có hỗ trợ gốc:
function generateUuidV7() {
const buf = new Uint8Array(16)
crypto.getRandomValues(buf)
const ms = BigInt(Date.now())
// Embed 48-bit Unix ms timestamp
buf[0] = Number((ms >> 40n) & 0xFFn)
buf[1] = Number((ms >> 32n) & 0xFFn)
buf[2] = Number((ms >> 24n) & 0xFFn)
buf[3] = Number((ms >> 16n) & 0xFFn)
buf[4] = Number((ms >> 8n) & 0xFFn)
buf[5] = Number(ms & 0xFFn)
// Set version 7 (0111xxxx)
buf[6] = (buf[6] & 0x0F) | 0x70
// Set variant (10xxxxxx)
buf[8] = (buf[8] & 0x3F) | 0x80
const hex = [...buf].map(b => b.toString(16).padStart(2, '0')).join('')
return `${hex.slice(0,8)}-${hex.slice(8,12)}-${hex.slice(12,16)}-${hex.slice(16,20)}-${hex.slice(20)}`
}
// Node.js 20+ built-in
// import { randomUUID } from 'node:crypto' // v4 only — no v7 yet in stdlib# pip install uuid7 import uuid_extensions uid = uuid_extensions.uuid7() print(uid) # e.g. 018e2b3d-1a2b-7000-8000-abc123456789 print(uid.time) # Unix ms timestamp embedded in the UUID # Or as a plain string from uuid_extensions import uuid7str print(uuid7str())
-- PostgreSQL 13+ extension-free implementation
CREATE OR REPLACE FUNCTION uuid_generate_v7()
RETURNS uuid
LANGUAGE sql
AS $$
SELECT encode(
set_bit(
set_bit(
overlay(
uuid_send(gen_random_uuid())
PLACING substring(int8send(floor(extract(epoch FROM clock_timestamp()) * 1000)::bigint) FROM 3)
FROM 1 FOR 6
),
52, 1
),
53, 1
),
'hex'
)::uuid;
$$;
-- Usage as a default primary key
CREATE TABLE events (
id uuid PRIMARY KEY DEFAULT uuid_generate_v7(),
payload jsonb,
created_at timestamptz DEFAULT now()
);function extractTimestamp(uuid: string): Date {
const hex = uuid.replace(/-/g, '')
const ms = parseInt(hex.slice(0, 12), 16) // first 48 bits = ms timestamp
return new Date(ms)
}
const uid = '018e2b3d-1a2b-7000-8000-abc123456789'
console.log(extractTimestamp(uid).toISOString())
// → "2024-03-15T10:22:05.259Z"