PostgreSQL 透過內建的 gen_random_uuid() 函式產生 UUID,該函式會回傳由 RFC 4122 所定義、並由 RFC 9562 更新的版本 4 UUID,在 PostgreSQL 13 之後無需安裝任何擴充套件。此函式從伺服器上密碼學安全的來源擷取 122 個隨機位元,將版本與變體位元固定以符合規範,並回傳標準的 8-4-4-4-12 十六進位字串,例如 f47ac10b-58cc-4372-a567-0e02b2c3d479。原生 uuid 欄位型別接受相同的格式,因此結果可以直接儲存,不需進行型別轉換或處理。有兩種途徑:內建函式 (gen_random_uuid() 及其別名 uuidv4()),以及較舊的 uuid-ossp contrib 模組,它提供了 uuid_generate_v1、uuid_generate_v4、uuid_generate_v5 以及其他幾個相關的呼叫。新建立的應用程式應將內建的隨機函式作為預設,因為它隨時可用、不需要 CREATE EXTENSION,且能產生與現代網路技術堆疊中其他元件完全相同格式的識別碼。本指南將逐步介紹這兩種途徑,說明如何將該函式設為欄位的 DEFAULT,讓新增資料時不需要提供值,並說明在什麼情況下於資料庫外部產生 UUID 是更乾淨的選擇。

在 PostgreSQL 中產生 UUID 的兩種途徑
PostgreSQL 透過兩個介面提供 UUID 產生功能。第一個是隨伺服器本身一起出貨的內建函式對,不需要安裝;第二個是稱為 uuid-ossp 的 contrib 模組,它新增了較舊的、針對特定演算法的呼叫,在 PostgreSQL 13 之前的版本中仍然有用。內建函式是建議新工作使用的預設選項,因為它們隨時可用、不需要超級使用者權限即可啟用,且產生的識別碼符合其他每種語言和函式庫所稱的「UUID」。
| 方法 | 函式 | 輸出 | 設定 | PostgreSQL 版本 |
|---|---|---|---|---|
| 內建 | gen_random_uuid() | 版本 4 UUID (隨機) | 無 | 13 及之後 |
| 內建別名 | uuidv4() | 版本 4 UUID (隨機) | 無 | 13 及之後 |
| uuid-ossp | uuid_generate_v4() | 版本 4 UUID (隨機) | CREATE EXTENSION uuid-ossp | 所有支援版本 |
| uuid-ossp | uuid_generate_v1() | 版本 1 UUID (時間 + MAC) | CREATE EXTENSION uuid-ossp | 所有支援版本 |
| uuid-ossp | uuid_generate_v5(namespace, name) | 版本 5 UUID (SHA-1 命名空間) | CREATE EXTENSION uuid-ossp | 所有支援版本 |
兩個隨機變體 — gen_random_uuid() 與 uuid_generate_v4() — 產生相同的格式:32 個十六進位數字,其中版本半位元組設為 4,變體半位元組設為 8、9、a 或 b。兩者唯一的差別在於達成該格式的方式,因為其中一個隨時可用,另一個則需要安裝 contrib 模組。
在 SQL 中使用 gen_random_uuid()
內建函式可以在任何允許值運算式的位置呼叫:在 SELECT 中、在 INSERT 內部、作為 DEFAULT 的一部分,或在 RETURNING 子句中。以下步驟涵蓋常見的用例。
- 在 psql 或您選擇的用戶端中執行 SELECT version();,以確認您的伺服器為 PostgreSQL 13 或之後的版本。
- 產生單一 UUID 以查看其標準格式:SELECT gen_random_uuid();。結果是一個以連字號分隔的小寫字串,共 32 個十六進位數字,例如 f47ac10b-58cc-4372-a567-0e02b2c3d479。
- 透過在原本應提供值的位置傳入函式呼叫,將該值插入到 uuid 欄位中:INSERT INTO devices (id, name) VALUES (gen_random_uuid(), 'sensor-01');。
- 若您已有需要載入的值,可將字串字面值轉型為 uuid:INSERT INTO devices (id, name) VALUES ('f47ac10b-58cc-4372-a567-0e02b2c3d479'::uuid, 'sensor-01');。
- 在 DEFAULT 子句或生成欄位中使用該函式,讓每筆新資料列自動取得值,而無需修改發出 insert 的應用程式程式碼。
若要批次產生,可在 generate_series 內重複呼叫。以下查詢會在一次往返中回傳 10 個全新的 UUID:SELECT gen_random_uuid() FROM generate_series(1, 10);。相同做法可擴展至您需要的任何規模,並完全留在資料庫內執行。
在較舊的 PostgreSQL 版本中啟用 uuid-ossp
執行 PostgreSQL 12 或更舊版本的伺服器,其核心發行版中並未包含 gen_random_uuid(),而需要版本 1 (時間戳加上 MAC)、版本 3 或版本 5 識別碼的部署,則需依賴 uuid-ossp contrib 模組。針對每個資料庫執行一次 CREATE EXTENSION 來安裝,然後呼叫對應到所需版本的函式即可。
CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; SELECT uuid_generate_v4();
此擴充套件提供 uuid_generate_v1()、uuid_generate_v1mc()、uuid_generate_v3()、uuid_generate_v4() 和 uuid_generate_v5()。uuid_generate_v4() 是最接近 gen_random_uuid() 的等價物,也是一般主鍵的正確選擇。uuid_generate_v1() 會產生帶有時間戳與主機 MAC 位址的時間排序識別碼,這可能會洩漏網路資訊,因此在新工作中很少作為預設。uuid_generate_v5() 在給定命名空間 UUID 與名稱字串時是確定性的,這使得它適用於必須跨系統可重現的識別碼。
若擴充套件安裝失敗並出現「could not open extension control file」錯誤,表示伺服器安裝中缺少 contrib 套件。在 Debian 與 Ubuntu 上是 postgresql-contrib;在 RHEL 及其衍生版本上則是對應的 postgresql-contrib RPM。套件就位後,CREATE EXTENSION 即可在不重新啟動伺服器的情況下運作。
將產生的 UUID 設為主鍵 DEFAULT
選擇 gen_random_uuid() 最常見的原因,是在不強迫每筆插入動作自行產生識別碼的情況下,填入主鍵欄位。PostgreSQL 允許您將該函式連接至欄位定義,讓資料庫自動指派值。
CREATE TABLE orders ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), customer_email text NOT NULL, created_at timestamptz NOT NULL DEFAULT now() ); INSERT INTO orders (customer_email) VALUES ('[email protected]');
這段 INSERT 從未指定 id 欄位,但產生的資料列仍帶有一個剛產生的版本 4 UUID。此模式適用於任何應始終帶有值的 uuid 欄位,包括需要由資料庫而非用戶端填入的外鍵欄位。若因 B-tree 局部性而需要時間排序鍵,較新版本 PostgreSQL 發行版與 contrib 模組中提供的 uuidv7() 函式能在維持相同欄位型別的同時,產生可排序的識別碼。
uuid 主鍵比 4 位元組的 serial 或 8 位元組的 bigserial 更寬,因此索引也會相應成長。對大多數工作負載而言,這樣的權衡是值得的:隨機鍵可讓您在用戶端產生識別碼、在合併複本時不發生主鍵衝突,並避免因可預測的序列而洩漏資料筆數。將 UUID 與租戶識別碼組合的複合主鍵,亦遵循相同的 DEFAULT 模式。
在 PostgreSQL 外部產生 UUID 以供測試與遷移使用
有些任務在到達資料庫之前就需要 UUID 值 — 例如固定資料檔、種子腳本、模擬 API,或是透過 COPY 載入的預備資料。在應用層或瀏覽器中產生它們,可讓資料庫不必介入,並讓您預覽最終進入欄位的確切文字內容。
一個實用的做法是使用 UUID 產生器,它透過瀏覽器的 crypto.getRandomValues CSPRNG,一次產生 1 到 100 個 RFC 4122 v4 UUID。每個輸出的第 13 個十六進位數字為 4,第 17 個為 8、9、a 或 b,因此這些字串在結構上與 gen_random_uuid() 所回傳的完全相同。您可以切換為大寫或去除連字號,以符合欄位所預期的文字格式,然後在不打斷編輯流程的情況下,直接將值貼到 .sql 檔案或 COPY 承載中。
這也是為不應出現在記錄檔或分析中的識別碼建立種子的安全方式:每個值都在瀏覽器分頁中於本機產生,無需與伺服器往返,因此這些字串可直接放進版本控制的固定資料中,而不會將內部識別碼暴露給遠端服務。就一次性測試資料而言,工作流程相當快速 — 選擇數量、點擊一次、複製一次 — 當需要更大批次時,也可一次擴展至 100 個 UUID。
UUID 版本與 gen_random_uuid() 為何是隨機的
UUID 是一個 128 位元的標籤,以標準的 8-4-4-4-12 格式寫成 32 個十六進位數字。在這 128 個位元中,有 6 個由規範固定:其中 4 個用於編碼版本 (因此版本 4 UUID 的第 13 個十六進位數字始終為 4),2 個用於編碼變體 (因此第 17 個十六進位數字始終為 8、9、a 或 b)。其餘 122 個位元則承載各版本特有的資料,對版本 4 而言,該資料即為純隨機性。
| UUID 版本 | 位元如何衍生 | 典型用途 |
|---|---|---|
| 1 | 60 位元時間戳 + MAC 位址 (或隨機節點) | 希望使用可排序 ID 的舊有系統 |
| 3 | 命名空間 + 名稱的 MD5 雜湊 | 由已知名稱衍生的確定性 ID (舊有) |
| 4 | 122 個隨機位元 | 新應用程式的預設 (gen_random_uuid) |
| 5 | 命名空間 + 名稱的 SHA-1 雜湊 | 確定性 ID (優先於 v3) |
| 7 | 48 位元毫秒時間戳 + 隨機尾段 | 具 B-tree 局部性的可排序 ID |
這 122 個隨機位元可產生 2^122 ≈ 5.3 × 10^36 種可能的 UUID,這就是為何版本 4 在實務上被視為實際上不會碰撞的原因。完整的規則與固定位元定義於 RFC 4122 及其 2024 年的取代文件 RFC 9562,gen_random_uuid() 即符合這些規範。版本 4 是大多數 PostgreSQL 主鍵的正確預設,因為這些識別碼不需要時鐘、不需要 MAC 位址、也不需要命名空間:它們既無法猜測,且在統計上彼此獨立,這正是規範對於「無需協調即可唯一」此一保證所承諾的特性。