> uuidv7 generator <
// 依時間排序、可排序的 UUID(RFC 9562,2024)— 48 位元 Unix 毫秒時間戳 + 74 位元隨機值,在本機產生
// 解碼 / 檢視 UUIDV7
在下方貼上任何 UUID 以擷取其內嵌的時間戳、版本與變體。可處理 v7,若輸入為其他 UUID 版本則會回報。
符合規範的 UUIDv7
實作 RFC 9562(2024 年 5 月)的依時間排序 UUID 格式:48 位元 Unix 毫秒時間戳、4 位元版本 7、12 位元 rand_a、2 位元變體、62 位元 rand_b。已針對規範的測試向量驗證。
時間順序的排序
以數值或字典序排序即可得到插入順序 — 非常適合 B 樹主鍵、時間序列索引與日誌關聯。不再有隨機的 v4 頁面分裂或碎片化索引。
同一毫秒內的單調性
當多個 UUID 落在同一毫秒時,隨機部分會嚴格遞增,使輸出在該毫秒內保持排序。若偏好,可關閉以使用單純的 RFC 9562 方法 1(隨機填充)。
檢視任何 UUIDv7
貼上 v7 UUID,解碼器即可擷取內嵌的時間戳(毫秒 + ISO 8601)、版本位元、變體位元,以及 rand_a / rand_b 欄位。適用於除錯、日誌考古與稽核軌跡。
// 關於 UUIDV7
UUIDv7 如何運作:
UUIDv7 是一個 128 位元識別碼,配置為 unix_ts_ms (48) | ver (4) | rand_a (12) | var (2) | rand_b (62)。前 48 位元是以毫秒計的目前 Unix 時間戳,採 big-endian,使 UUID 在時間上單調遞增。接下來的 4 位元編碼版本(二進位 0111 = 十進位 7),因此以連字號分隔的第三組總是以數字 7 開頭。rand_a 是 12 個隨機位元,用於次毫秒解析度或作為額外的熵。2 位元的變體欄位固定為 10(因此第四組的第一個字元總是 8、9、a 或 b)。其餘 62 位元(rand_b)在每次產生時來自 crypto.getRandomValues()。每個 UUID 的總熵為 74 個隨機位元,對任何實際的產生速率都具有抗碰撞性。
範例:
018f5c2e-90c0-7a4f-b6ee-8dfe3c2b0a55 (ts=2024-05-08T15:00:00.000Z)
標準與參考資料:
- >RFC 9562 — Universally Unique IDentifiers (UUIDs),2024 年 5 月(取代 RFC 4122,定義 v6/v7/v8)
- >RFC 4122 — 2005 年原始的 UUID 規範(對 v1-v5 仍具權威性)
- >Web Crypto API(W3C)— 用於 74 個隨機位元的
crypto.getRandomValues()CSPRNG - >PostgreSQL
gen_random_uuid()的行為(預設為 v4,pgcrypto 擴充 / 分支中為 v7) - >draft-peabody-dispatch-new-uuid-format — 成為 RFC 9562 的原始 v6/v7/v8 提案
為何使用 UUIDv7:
- >具插入局部性的資料庫主鍵 — 頁面保持密實,索引不會碎片化
- >取代序列 + UUID 的組合:一個欄位即可排序且全域唯一
- >取代寫入繁重資料表中的 v4 主鍵,解決隨機插入造成的頁面分裂問題
- >依建立時間排序的日誌關聯 ID,無需另設時間戳欄位
- >多個寫入者需要在無協調者情況下取得有序 ID 的分散式系統
- >以時間順序為自然索引的事件溯源 / 僅追加日誌
- >前綴排序與插入順序一致的 S3 / 物件儲存鍵
- >在偏好標準格式之處取代 Snowflake / KSUID / ULID
相關工具:
// 輸出範例
現在產生的單一 UUIDv7
count=1, format=standard, timestamp=current
output:
018f5c2e-90c0-7a4f-b6ee-8dfe3c2b0a55
同一毫秒內具單調性的批次產生
count=5, format=standard, monotonic=on
output:
018f5c2e-90c0-7a4f-b6ee-8dfe3c2b0a55 018f5c2e-90c0-7a4f-b6ee-8dfe3c2b0a56 018f5c2e-90c0-7a4f-b6ee-8dfe3c2b0a57 018f5c2e-90c0-7a4f-b6ee-8dfe3c2b0a58 018f5c2e-90c0-7a4f-b6ee-8dfe3c2b0a59
自訂歷史時間戳 — 回填遷移
count=1, timestamp=custom, value=1577836800000 (2020-01-01)
output:
016f5e66-e800-7c9a-b403-2f1d4a7c91ee
適用於 URL / 檔名的精簡格式
count=1, format=no-hyphens
output:
018f5c2e90c07a4fb6ee8dfe3c2b0a55
解碼的 UUIDv7 — 檢視內嵌欄位
input = 018f5c2e-90c0-7a4f-b6ee-8dfe3c2b0a55
output:
ts_ms = 1715180400000 ISO = 2024-05-08T15:00:00.000Z ver = 7 var = 10 (RFC 4122) rand_a = 0xa4f rand_b = 0x36ee8dfe3c2b0a55
// 自行實作
自 RFC 9562 於 2024 年問世以來,大多數語言對於 UUIDv7 不是有標準函式庫的輔助函式,就是有單檔實作。以下是可直接放入程式碼庫的標準範例 — 包含瀏覽器 JS、Node.js、Go、Python 與 PostgreSQL。
純瀏覽器/Node JS 中的 UUIDv7 — RFC 9562 配置
function uuidv7() {
const ts = BigInt(Date.now());
const rand = new Uint8Array(10);
crypto.getRandomValues(rand);
const b = new Uint8Array(16);
b[0] = Number((ts >> 40n) & 0xFFn);
b[1] = Number((ts >> 32n) & 0xFFn);
b[2] = Number((ts >> 24n) & 0xFFn);
b[3] = Number((ts >> 16n) & 0xFFn);
b[4] = Number((ts >> 8n) & 0xFFn);
b[5] = Number( ts & 0xFFn);
b[6] = (rand[0] & 0x0F) | 0x70; // version = 7
b[7] = rand[1];
b[8] = (rand[2] & 0x3F) | 0x80; // variant = RFC 4122
for (let i = 9; i < 16; i++) b[i] = rand[i - 6];
const h = Array.from(b, x => x.toString(16).padStart(2,'0')).join('');
return `${h.slice(0,8)}-${h.slice(8,12)}-${h.slice(12,16)}-${h.slice(16,20)}-${h.slice(20)}`;
}
// usage:
const id = uuidv7();
console.log(id); // -> '018f5c2e-90c0-7a4f-b6ee-8dfe3c2b0a55'
// generate a batch and verify chronological sort:
const batch = Array.from({ length: 5 }, uuidv7);
batch.sort(); // sorts by creation time without a separate timestamp
Node.js v26.1.0 以上 — 內建的 crypto.randomUUIDv7()
import { randomUUIDv7 } from 'node:crypto';
// Added in Node.js v26.1.0 (2026-05-07).
// Returns an RFC 9562 version 7 UUID. The first 48 bits encode a
// millisecond Unix timestamp; the rest is CSPRNG output.
const id = randomUUIDv7();
// -> '018f5c2e-90c0-7a4f-b6ee-8dfe3c2b0a55'
// The CSPRNG output is cached for performance: Node generates enough
// entropy upfront for the next ~128 UUIDs. Pass disableEntropyCache: true
// when you need each call to draw fresh randomness, e.g. directly after
// a fork() or in tight high-security loops.
const freshId = randomUUIDv7({ disableEntropyCache: true });
// IMPORTANT: per the Node docs, "the embedded timestamp relies on a
// non-monotonic clock and is not guaranteed to be strictly increasing."
// If two UUIDs land in the same millisecond OR the system clock steps
// backwards, the IDs may not sort in creation order. Use a userland
// monotonic-v7 library (e.g. uuidv7 on npm) when strict ordering matters.
Go — google/uuid v1.6 以上原生提供 UUIDv7
package main
import (
"fmt"
"github.com/google/uuid"
)
func newID() uuid.UUID {
id, err := uuid.NewV7()
if err != nil {
panic(err)
}
return id
}
func main() {
id := newID()
fmt.Println(id.String()) // 018f5c2e-90c0-7a4f-b6ee-8dfe3c2b0a55
fmt.Println(id.Time()) // embedded creation timestamp
}
Python 3.13 以上 — 標準函式庫 uuid.uuid7()
import uuid
# Python 3.13 added uuid7() and uuid8() to the standard library.
uid = uuid.uuid7()
print(uid) # 018f5c2e-90c0-7a4f-b6ee-8dfe3c2b0a55
print(uid.version) # 7
# For older Python use the 'uuid7' or 'uuid-utils' PyPI packages, or:
# from uuid_extensions import uuid7
PostgreSQL 18 以上 — 原生 uuidv7() 函式
-- PG 18 ships uuidv7() natively. Use it as a primary key default:
CREATE TABLE events (
id uuid PRIMARY KEY DEFAULT uuidv7(),
body jsonb,
ts timestamptz NOT NULL DEFAULT now()
);
-- For older Postgres, install the pg_uuidv7 extension or generate client-side.
-- Migration tip: keep INDEX on (id) only — v7 already sorts chronologically,
-- so a separate (created_at) index is usually redundant.
// 常見陷阱
> UUIDv7 會洩漏建立時間 — 請勿用於不透明權杖
每個 v7 的前 48 位元都是 Unix 毫秒時間戳。任何看到該 UUID 的人都能解碼出記錄的建立時間、ID 的核發速率,並(在多個樣本下)推估伺服器時鐘。這對內部資料庫主鍵與日誌 ID 沒問題,但對於密碼重設 URL、分享連結、工作階段 ID 或邀請碼,請改用 UUIDv4 或由 CSPRNG 產生的隨機字串。
> 時鐘回退會破壞單調性
若系統時鐘向後跳(NTP 步進、VM 遷移、閏秒),天真產生的 v7 UUID 可能產生排序在先前核發 ID 之前的值。RFC 9562 §6.2 建議方法 2(單調隨機):追蹤最後產生的 UUID,當時間戳未前進時,改為遞增隨機部分。本工具的單調開關正是這麼做的。
> 在對安全敏感的情境中,v7 並非 v4 的直接替代品
看似隨機的 ID(v4)常被誤用為持有者權杖:「知道 UUID 就能讀取該列」。v7 只有 74 個隨機位元並帶有可預測的時間戳前綴,雖然仍具抗碰撞性,但提供的不可預測性遠低於 v4 的 122 個隨機位元。若某項內容被當作能力(capability)看待,請產生獨立的密碼學權杖,而非重複使用 v7 主鍵。
> 字串比較僅在小寫、含連字號的形式下有效
v7 的時間順序排序依賴標準 36 字元小寫字串的字典序比較 — 或底層 16 位元組值的逐位元組比較。混用大小寫、中途去除連字號,或把某些列以 {...} 大括號儲存而其他不加,都會產生不再符合建立時間的排序順序。請為每個欄位選定一種標準形式,並以 CHECK 約束強制執行。
> 48 位元時間戳會在西元 10889 年回繞
2^48 毫秒約為 Unix 紀元之後 8,925 年 — 時間戳欄位會在 10889 年 8 月 2 日 UTC 溢位。若你的程式碼斷言內嵌時間戳落在合理範圍內,請將上限設為 281,474,976,710,655 毫秒(48 位元無號整數可容納的最大值),而非寫死一個近未來的年份,因為它在一個世紀後會出錯。
>> 常見問題
問:什麼是 UUIDv7?
答:UUIDv7 是定義於 RFC 9562(2024 年 5 月)的 128 位元識別碼,結合 48 位元 Unix 毫秒時間戳、74 位元的隨機性,以及標準的版本 + 變體位元。其結果是一個全域唯一的 ID,同時也能依建立時間進行時間順序排序 — 兩全其美。它是專為解決隨機 UUIDv4 主鍵所造成的資料庫索引痛點而設計的,在該情境下每次插入都落在不同的 B 樹頁面,造成大量的寫入放大。
問:UUIDv7 與 UUIDv4 有何不同?
答:UUIDv4 完全隨機(122 個隨機位元),除版本 + 變體欄位外沒有任何結構。相隔一微秒產生的兩個 v4 會排序到天差地遠的位置,這對不可預測性很好,但對 B 樹索引很糟。UUIDv7 將 48 位元 Unix 毫秒時間戳放在前面,因此時間相近產生的 v7 在儲存上也排序相近。權衡:v7 會揭露建立時間,且只有 74 個隨機位元而非 122 個 — 仍具抗碰撞性,但不適合作為祕密權杖。
問:我應該從 UUIDv4 遷移到 UUIDv7 嗎?
答:對於寫入繁重資料表的內部資料庫主鍵 — 是的,幾乎總是如此。v4 造成的索引碎片化可能遠超查詢本身的成本,尤其在使用 InnoDB 叢集索引的 PostgreSQL 或 MySQL 上。對於暴露給使用者、嵌入 URL 或被當作能力看待的權杖(「知道 UUID 就能存取資源」),請保留 v4 或使用獨立的隨機權杖 — v7 會洩漏建立時間。常見模式是內部聯結使用 v7 主鍵,再為對外的 URL 另設一個獨立的隨機 slug。
問:UUIDv7 與 ULID、KSUID 或 Snowflake 相同嗎?
答:概念上相似 — 這四者都是依時間排序的 ID — 但位元配置與編碼不同。ULID 使用 Crockford base32 與不同的毫秒配置。KSUID 將 32 位元時間戳 + 128 位元酬載編碼為 27 個 base62 字元。Snowflake 是帶有工作者 ID 的 64 位元 Twitter 專用 ID。UUIDv7 是 IETF 標準的對應物,以標準的 8-4-4-4-12 十六進位形式編碼,因此每個 UUID 函式庫、資料庫 UUID 型別與 JDBC/ODBC 驅動程式都已能理解。若你在 2025 年以後從頭開始,請優先選擇 v7 — 它是標準。
問:在發生碰撞前,每毫秒能產生多少個 UUIDv7?
答:在單一毫秒內,只有 74 個隨機位元(rand_a 中的 12 個 + rand_b 中的 62 個)提供唯一性。依生日碰撞界限,在 50% 碰撞機率之前,你可在同一毫秒內產生約 2^37 ≈ 1,370 億個 v7 — 對任何實際系統而言實質上無上限。跨毫秒時,時間戳本身即可區分,因此系統整個生命週期的全域碰撞風險可忽略不計。開啟單調開關後,毫秒內的碰撞依設計為零(隨機部分會遞增)。
問:我能從 UUIDv7 擷取時間戳嗎?
答:可以 — 時間戳是前 48 位元,也就是去除連字號後的前 12 個十六進位字元。串接前三個連字號群組(8 + 4 = 12 hex digits),以十六進位整數解析,即可得到以毫秒計的 Unix 時間戳。可使用上方的解碼器以互動方式進行,或在程式碼中:parseInt(uuid.replace(/-/g,'').slice(0,12), 16)。結果即為 UUID 產生時的毫秒時刻,精確到當時的系統時鐘。
問:UUIDv7 中的版本數字長什麼樣子?
答:以連字號分隔的第三組總是以數字 7 開頭。例如:018f5c2e-90c0-7a4f-b6ee-8dfe3c2b0a55。第四組總是以 8、9、a 或 b 開頭 — 這是高位兩個位元為 10(RFC 4122 變體)時的四個可能值。若版本數字不是 7,則該 UUID 為其他版本(隨機通常為 4,或基於時間的 v1 為 1),且不會依時間順序排序。
問:PostgreSQL / MySQL 原生支援 UUIDv7 嗎?
答:PostgreSQL 18(2025 年 9 月發布)以內建函式提供 uuidv7()。較早版本需要 pg_uuidv7 擴充或在用戶端產生。MySQL 8.x 沒有原生 v7;UUID_TO_BIN(uuid, 1) 輔助函式(為排序順序而交換時間戳位元組)僅適用於 v1 — v7 請在用戶端產生。SQL Server、Oracle 與 SQLite 都預期在用戶端產生 v7。大多數 ORM(Hibernate、Entity Framework、Sequelize、Prisma、SQLAlchemy)現在都以原生或外掛方式支援 v7。
問:單調選項符合 RFC 9562 嗎?
答:是的。RFC 9562 §6.2 明確描述了三種確保毫秒內順序的方法:(1) 隨機填充,(2) 單調隨機 — 當時間戳未前進時遞增計數器,(3) 次毫秒時間戳精度。本工具中的單調核取方塊實作了方法 2:當兩個 UUID 共用相同的 48 位元時間戳時,第二個的隨機部分會被設為 previous_random + 1,而非重新抽取。這在完全符合規範的同時,保證了批次產生的嚴格順序。
問:我的資料會被傳送到任何地方嗎?
答:不會。所有 UUIDv7 的產生、解碼、排序、複製與下載都完全在你的瀏覽器中以純 JavaScript 進行。當你按下產生、解碼、複製或下載時,頁面絕不會發出網路請求 — 你可用瀏覽器的 DevTools 網路分頁驗證。我們不記錄時間戳、自訂種子、產生的 UUID 或解碼的輸出。頁面本身是由 CDN 提供的靜態 HTML,因此任何輸入都不會抵達我們的基礎設施。這使本工具可安全用於正式環境的資料庫 ID、內部日誌,或任何你寧可不經過第三方伺服器的資料。
問:我能在 Java / .NET / Rust 中使用 UUIDv7 嗎?
答:可以。Java 有 com.fasterxml.uuid:java-uuid-generator 5.0+,而 JDK 21 可透過函式庫輔助函式使用。.NET 9 在 BCL 中提供 Guid.CreateVersion7()。Rust 在 v7 功能旗標下使用 uuid crate 的 Uuid::now_v7()。大多數其他生態系(Elixir、Ruby、PHP、Swift、Kotlin)也有維護良好的套件 — 請在該語言的套件登錄處搜尋「uuidv7」或「uuid v7」。線路格式在所有語言中皆相同,因此在 Go 中產生的 v7 可由 JavaScript 解析並擷取時間戳,無需任何轉換。
問:Node.js 有內建的 UUIDv7 產生器嗎?
答:有 — 自 Node.js v26.1.0(2026-05-07 發布)起,標準函式庫公開了 crypto.randomUUIDv7([options])。以 import { randomUUIDv7 } from 'node:crypto' 匯入,並在不帶引數的情況下呼叫,即可取得 RFC 9562 v7 UUID 字串。唯一支援的選項是 disableEntropyCache(布林值,預設 false);預設情況下,Node 會預先快取足以產生接下來約 128 個 UUID 的隨機資料,這與 crypto.randomUUID() 使用的最佳化相同。官方文件的重要警語:「內嵌的時間戳依賴非單調時鐘,不保證嚴格遞增」。換言之,在同一毫秒內產生的兩個 UUID,或在 NTP 步進 / VM 時鐘向後偏移期間建立的任何 UUID,可能不會依建立順序排序。若你需要毫秒內的嚴格單調性,請使用遵循 RFC 9562 §6.2 方法 2 的 userland 實作(npm 的 uuidv7 套件,或開啟單調開關的本工具)。
問:如何在資料庫中高效儲存 UUIDv7?
答:以 16 位元組的二進位型別儲存,而非 36 字元的字串 — 每列可省下 20 位元組、索引大小減半,並獲得更快的比較路徑。請使用 PostgreSQL 的 uuid 型別、MySQL BINARY(16)、SQL Server uniqueidentifier 或 SQLite BLOB。若想強制執行,可加上版本 nibble 等於 7 的 CHECK 約束。使用 v7 時,列會自然依建立時間叢集,因此通常在 (uuid_pk) 上建立涵蓋索引即可 — 不需要為時間順序查詢另設 (created_at) 索引。