Uuid

**通用唯一辨識碼**（英語：**U**niversally **U**nique **Id**entifier，縮寫：**UUID**）是用於電腦體系中以辨識資訊的一個 128 位元識別碼。

UUID 按照標準方法生成時，在實際應用中具有唯一性，且不依賴中央機構的註冊和分配。UUID 重複的概率接近零，可以忽略不計。

因此，所有人都可以自行建立和使用 UUID，而且幾乎可以確定其不會與既有的識別碼重複。也因為如此，在不同地方產生的 UUID 可以使用於同一個資料庫或同一個頻道中，而且幾乎不可能重複。

UUID 的應用相當普遍，許多計算平台都提供了對於生成和解析 UUID 的支援。

## 歷史

1990 年代， UUID 原本是用於阿波羅電腦的網路計算系統，後被用於開放軟體基金會的分散式運算環境。分散式運算環境 UUID 的初始設計基於網路計算系統 UUID，其設計受 Domain/OS 中定義和使用的（64 位元）唯一識別碼的啟發，這是一個也由 阿波羅電腦 設計的作業系統。後來，微軟視窗平台採用分散式運算環境設計作為全域唯一識別碼（GUID）。

2005 年 7 月，RFC 4122 為 UUID 註冊了一個 URN 命名空間，並制定了早期的規範。當 RFC 4122 作為網際網路工程任務組標準發布時，國際電信聯盟基於先前的標準和 RFC 4122 早期版本標準化了 UUID。

## 標準

UUID 由開放軟體基金會標準化，作為分散式運算環境（DCE）的一部分。

UUID 被紀錄為 ISO/IEC 11578:1996 "Information technology – Open Systems Interconnection – Remote Procedure Call（RPC）" 和後來的 ITU-T Rec. X.667 | ISO / IEC 9834-8：2005 規範的一部份。

網際網路工程任務組公布了標準 RFC 4122，技術上等同於 ITU-T Rec. X.667 | ISO/IEC 9834-8。

## 格式

在其規範的文字表示中，UUID 的 16 個 8 位位元組表示為 32 個十六進位數字，由連字元 '-' 分隔成五組顯示，形式為「8-4-4-4-12」總共 36 個字元（32 個十六進位數字和 4 個連字元）。

例如：

　
: `123e4567-e89b-12d3-a456-426655440000`

　
: `xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx`

四位數字 `M` 表示 UUID 版本，數字 `N` 的一至三個最高有效位表示 UUID 變體。在例子中，_M_ 是 `1` 而且 _N_ 是 `a`（`10xx2`），這意味著此 UUID 是「變體 1」、「版本 1」UUID；即基於時間的 DCE/RFC 4122 UUID。

規範的 `8-4-4-4-12` 格式字串基於 UUID 的 16 個位元組的「記錄布局」：

這些欄位對應於「版本 1」和「版本 2」（基於時間的）UUID 中的欄位，但是「8-4-4-4-12」的表示適用於所有 UUID，即使對於生成方式不同的 UUID 也是如此。

RFC 4122 第 3 節要求以小寫形式生成字元，同時對輸入不區分大小寫，儘管一些常用的實現違反了此規則。

Microsoft GUID 有時會以大括號表示：

　
: `{123e4567-e89b-12d3-a456-426655440000}`

不應將此格式與「Windows 登錄檔格式」相混淆，後者指的是大括號內的格式。

RFC 4122 為 UUID 定義了統一資源名稱（URN）命名空間。作為 URN 呈現的 UUID 如下：

　
: `urn:uuid:123e4567-e89b-12d3-a456-426655440000`

## 編碼

UUID 的二進位編碼因系統而異。UUID 的變體 1 是目前世界最常見的 UUID，完全以大端序（big-endian）二進位儲存與傳輸 UUID 。

例如，`00112233-4455-6677-8899-aabbccddeeff` 編碼為位元組 `00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff`。

其他系統，特別是 Microsoft 在其 COM/OLE 庫中對 UUID 的字串表示，使用混合端格式，其中 UUID 的前三組是小端序 / 小尾序（little-endian），後兩組是 大端序 / 大尾序（big-endian）。

例如，`00112233-4455-6677-8899-aabbccddeeff` 編碼為位元組 `33 22 11 00 55 44 77 66 88 99 aa bb cc dd ee ff`。

## 變體

UUID 的**變體**（variant）欄位，占 1 到 3 位元。RFC 4122 定義了 4 種變體：

* 變體 0 (最顯著位為 0，二進制為 0xxx2，十六進制表示為 016 到 716) 用於向下相容已經過時的 1988 年開發的 Apollo 網路計算系統（NCS）1.5 UUID 格式。 前 6 位元組是 48 位元時間戳（從 1980 年 1 月 1 日 UTC 開始的 4 微秒的滴答數），隨後 2 個位元組保留，再 1 個位元組是位址族（address family，使用了 0..13 個情形），最後 7 個位元組是主機 ID。這類似於 UUID 的版本 1.
* 變體 1 (二進制為 10xx2，十六進制為 816 到 b16)，定義在 RFC 4122/DCE 1.1 UUIDs, 或 "Leach–Salz" UUID。它是按照大端序作為二進位儲存與傳輸。
* 變體 2 (二進制為 110x2，十六進制為 c16 或 d16)，RFC 稱「保留，微軟公司向下相容」。早期的 Microsoft Windows 平台的 GUID 使用該格式。和完全使用大端序的變體 1 不同，變體 2 其中一部份按照小端序作為二進位儲存與傳輸。
* 形如 111x2 保留未使用。

目前的 UUID 規範使用變體 1 和 2。在文字表示上，兩種變體只有代表變體的位元不同。上面的欄位也定義了兩種變體之間的位元組轉換。前三個欄位是無正負號的 32 和 16 位元數字，需要進行轉換，而後兩個欄位是由未解釋的位元組組成，不需要進行轉換。這種轉換同樣適用於版本 3、4 和 5，其中的規範欄位與 UUID 的內容無關。

雖然一些重要的 GUID 名義上是變體 2 的 UUID，例如元件物件模型（Component Object Model）IUnknown 介面的識別碼，但是在微軟 Windows 軟體中所生成和使用的、被稱為「GUID」的許多識別碼是使用標準的變體 1 的 RFC 4122/DCE 1.1 大端序 UUID。目前，Microsoft `guidgen` 工具軟體產生變體 1 的結果。某些微軟文件稱 GUID 與 UUID 是同義詞，如同 RFC 4122 中表示 UUID「也被稱作 GUID」。這些檔案表明了雖然「GUID」最初指代微軟所使用的其中一種 UUID 變體，但現在已經成為 UUID 的另一個名稱，含括變體 1 和 2。

## 版本

對於「變體（variants）1」和「變體 2」，標準中定義了五個版本（versions），並且在特定用例中某些版本可能比其他版本更合適。

版本由字串中的 M 指示。

版本 1 的 UUID 是根據時間和節點 ID（通常是 MAC 位址）生成；版本 2 的 UUID 是根據識別碼（通常是組或使用者 ID）、時間和節點 ID 生成；版本 3、版本 5 透過對命名空間（namespace）識別碼和名稱進行雜湊生成確定性的 UUID；版本 4 的 UUID 則使用隨機性或偽隨機性生成。

### Nil UUID

Nil UUID 是一個特例，值為 `00000000-0000-0000-0000-000000000000` ；也就是說，所有位都設定為 0。

### 版本 1（日期時間和 MAC 位址）

版本 1 的 UUID，是根據 60-bit 的時間戳和節點（生成 UUID 的電腦）的 48-bit MAC 位址而生成的。

時間戳的是這樣計算的：自公曆首次於天主教會和教皇國以外的地方使用的日期，也就是協調世界時（UTC）1582 年 10 月 15 日午夜算起，每經過 100 奈秒時間戳加 1。RFC 4122 聲明時間值在公元 3400 年左右算術溢位，取決於所使用的演算法，代表此 60-bit 時間戳是有符號數量。但是，某些軟體（如 libuuid 庫）將時間戳視為無符號，把溢位時間推遲至公元 5236 年。ITU-T Rec. X.667 所定義的溢位時間為公元 3603 年。

13-bit 或 14-bit「無統一」（uniquifying）時鐘序列擴充了時間戳，以便處理處理器時鐘不能足夠快地前進的情況，或者每個節點有多個處理器和 UUID 生成器的情況。對於每個「版本 1」UUID 對應於空間（節點）和時間（間隔和時鐘序列）中的單個點，兩個正確生成的「版本 1」UUID 無意中相同的可能性實際上為零。由於時間和時鐘序列總共 74 位元，每個節點 id 可以生成 $2^{74}$ （ $1.8\times 10^{22}$ 或 18 sextillion）個「版本 1」UUID，每個節點 id 的最大平均速率為每秒 1630 億。

與其他 UUID 版本相比，基於來自網卡的 MAC 位址的「版本 1」和「版本 2」UUID，部分依賴於由中央序號產生器構發布的識別碼，即由 IEEE 發布給網路裝置製造商的 MAC 位址的組織唯一識別碼（OUI）。基於網卡 MAC 位址的「版本 1」和「版本 2」UUID 的唯一性還取決於網卡製造商正確地為其卡分配唯一的 MAC 位址，這與其他製造過程一樣容易出錯。此外，某些作業系統允許終端使用者自訂 MAC 位址，例如 OpenWRT。

使用節點的網路 MAC 位址作為節點 ID，代表可以透過版本 1 的 UUID 逆向找到建立它的電腦。透過在檔案中嵌入 UUID，可以實現追蹤到建立或修改這些檔案的計算機。在定位 Melissa 病毒的建立者時就使用了這個隱私漏洞。

如果節點沒有或不希望暴露 MAC 位址，RFC 4122 確實允許「版本 1」（或 2）UUID 中的 MAC 位址被隨機的 48 位元節點 ID 替換。在這種情況下，RFC 要求節點 ID 的第一個八位位元組的最低有效位應設定為 1，這對應於 MAC 位址中的多播位，設定它是用於區分隨機生成節點 ID 的 UUID 和基於來自網卡的 MAC 位址的 UUID，網卡通常具有單播 MAC 位址。

### 版本 2（日期時間和 MAC 位址，DCE 安全版本）

RFC 4122 保留了版本 2 的 UUID 用於「DCE security」；但並沒有提供任何細節。因此，許多 UUID 實現省略了「版本 2」。但是，「版本 2」UUID 的規範由 DCE 1.1 身分驗證和安全服務規範提供。

「版本 2」UUID 類似於「版本 1」，除了時鐘序列的最低有效 8 bits 被「本地域（local domain）」號替換，並且時間戳的最低有效 32 bits 由在指定本地域內有意義的整數識別碼替換。在 POSIX 系統上，本地域號 0 和 1 分別用於使用者 ID（UIDs）和組 ID（GIDs），其他本地域號用於站點定義。在非 POSIX 系統上，所有本地域號都是站點定義的。

在 UUID 中包含 40 位元的域或識別碼（domain/identifier）是有代價的。一方面，40 位元允許每個節點 ID 有大約 1 兆個域或識別碼的值。另一方面，由於時鐘值被截斷為 28 個最高有效位，有別於版本 1 中的 60 位元，版本 2 的 UUID 中的時鐘也改成每 429.49 秒跳動（tick）一次，略多於 7 分鐘，而不是版本 1 中的每 100 奈秒；並且，版本 2 的時鐘序列僅有 6 位元，版本 1 中則有 14 位元；每 7 分鐘時鐘周期內，每個節點、域或識別碼只能生成 64 個唯一的 UUID，而版本 1 的時鐘序列值為 16,384 個。因此，版本 2 可能不適合用於以節點、域或識別碼在約 7 秒以上 1 次的速率下生成 UUID 的情況。

### 版本 3 和版本 5（基於命名空間名稱）

「版本 3」和「版本 5」的 UUID 透過雜湊（hashing）命名空間識別碼和名稱生成。版本 3 使用 MD5 作為雜湊演算法，版本 5 則使用 SHA1。

名稱空間識別碼本身就是一個 UUID。該規範提供了 UUID 用來表示命名空間為了統一資源定位符（URLs），完整域名、物件識別碼和 X.500；但任何所需的 UUID 都可以用作命名空間指示符。

要確定與給定命名空間和名稱對應的版本 3 的 UUID，命名空間的 UUID 將轉換為位元組串，後面加上輸入名稱，然後用 MD5 進行雜湊，產生 128 位元。然後將六或七位替換為固定值，即 4 位元的版本號（例如「版本 3」的 0011），以及 2 或 3 位元的 UUID 變體號（例如 10 代表 RFC 4122 的 UUID，或 110 代表傳統 Microsoft GUID）。由於預定了 6 到 7 位元，因此只有 121 或 122 位元用於維持 UUID 的唯一性。

版本 5 的 UUID 和上面類似，但使用 SHA1 而不是 MD5。由於 SHA1 生成 160 位元的摘要，因此在替換版本號和變體號之前會把摘要截斷為 128 位元。

版本 3 和版本 5 的 UUID 具有一個特性：相同名稱空間和名稱將對映到同一個 UUID；然而，即使已知其中一項，也無法透過暴力搜尋之外的方法從 UUID 逆向推導出另外一項。RFC 4122 推薦使用版本 5（SHA1）而不是版本 3（MD5），並建議不要使用任一版本的 UUID 作為安全憑證。

### 版本 4（隨機）

版本 4 的 UUID 是隨機生成的。與其他 UUID 一樣，其中 4 位元用於代表「版本 4」，2 到 3 位元代表變體號（102 或 1102 分別用於變體 1 和 2）。因此，對於變體 1（即大多數 UUID），隨機生成的版本 4 的 UUID 會保留 6 位元用於表示變體號和版本號，其餘 122 位元用於隨機生成，故版本 4 變體 1 的 UUID 共計有 $2^{122}$ 或 $5.3\times 10^{36}$ （5.3 undecillion）個。版本 4 變體 2 的 UUID（傳統 GUID）則為變體 1 的一半，因為可用的隨機位少一個，變數消耗 3 位元。

一些偽亂數發生器缺少必要的熵來產生足夠的偽亂數。例如，使用偽亂數生成器的 WinAPI GUID 生成器已被證明可生成遵循可預測模式的 UUID。 RFC 4122 建議「在各種主機上生成 UUID 的分散式應用程式必須願意依賴所有主機上的亂數源。如果這不可行，則應使用名稱空間變體。」

## 衝突

當多次生成相同的 UUID 並將其分配給不同的指示物件時，就會發生衝突。對於使用來自網卡的唯一 MAC 位址的標準版本 1 和 2 的 UUID，只有當實施與標準不同時才可能發生衝突，無論是無意還是故意。

與使用 MAC 位址生成的版本 1 和版本 2 相比，使用隨機生成的節點 ID 的版本 1 和版本 2、基於雜湊的版本 3 和版本 5，以及隨機生成的版本 4 的 UUID，即使實現上沒有問題也可能發生衝突，但可能性很小，通常可以忽略。可以基於對生日問題的分析來精確地計算該概率。

例如，如果要有 50% 的機率至少發生一次衝突，需要生成至少 $2.71\times 10^{18}$ 個版本 4 的 UUID，計算如下：

$$
n\approx {\frac {1}{2}}+{\sqrt {{\frac {1}{4}}+2\times \ln(2)\times 2^{122}}}\approx 2.71\times 10^{18}
$$

這個數字相當於每秒產生 10 億個 UUID 持續 85 年。每個 UUID 長度為 16 位元組，這麼多 UUID 的檔案大小約為 45 艾位元組（EB），比目前存在的最大數據庫大很多倍，它們都在數百 PB 的數量級。

若要使發生衝突的機率為 _p_，至少必須生成多少個版本 4 的 UUID 可由下式近似計算：

$$
{\sqrt {2\times 2^{122}\times \ln {\frac {1}{1-p}}}}
$$

因此，在 103 萬億個版本 4 UUID 中找到重複的概率是 ${1 \over 1000000000}$ （十億分之一）。

## 使用

### 檔案系統

重要用途包括 ext2/ext3/ext4 檔案系統使用者空間工具（e2fsprogs 使用 util-linux 提供的 libuuid）、LUKS 加密分割區、GNOME、KDE 和 Mac OS X，其中大部分源自 曹子德（Theodore Ts'o）的實現。

Solaris 中 UUID 的一種用途（使用開放軟體基金會的實現）是辨識正在執行的作業系統實例，以便在核心崩潰的情況下將故障轉儲資料與故障管理事件配對。

分割區標籤和分割區 UUID 都儲存於超區塊中。兩者皆為檔案系統的一部份，而不是分割區的一部份。例如，ext2-ext4 包含 UUID，而 NTFS 或 FAT32 則沒有。

超區塊是檔案系統的一部份，因此被完全包含在分割區中，因此如果你執行 `dd if=/dev/sda1 of=/dev/sdb1`，sda1 和 sdb1 都會擁有相同的標籤和 UUID。

### COM

Microsoft 的組件物件模型（COM）中使用了幾種 GUID ：

* IID - 介面識別碼；（在系統上註冊的介面識別碼儲存在 Windows 登錄檔中的 `[HKEY\_CLASSES\_ROOT\Interface]`）
* CLSID - 類識別碼；（儲存在 `[HKEY\_CLASSES\_ROOT\CLSID]`）
* LIBID - 類型庫識別碼；（儲存於 `[HKEY\_CLASSES\_ROOT\TypeLib]`）
* CATID - 類別識別碼；（它在一個類中的存在將其辨識為屬於某些類別類別，列於 `[HKEY\_CLASSES\_ROOT\Component Categories]`）

### 作為資料庫主鍵

UUID 通常用作資料庫表中的唯一鍵。

Microsoft SQL Server 版本 4 Transact-SQL 中的 NEWID 函式會返回標準隨機版本 4 的 UUID，而 NEWSEQUENTIALID 函式返回類似於 UUID 的 128 位識別碼，這些 UUID 會依序遞增，直到下次系統重啟。

另一方面，儘管名稱如此，但 Oracle Database SYS\_GUID 函式不會返回標準 GUID；相反，它根據主機識別碼和行程或執行緒識別碼返回一個 16 位元組的 128 位 RAW 值，有點類似於 GUID。

PostgreSQL 包含一個 UUID 資料類型，並且可以通過使用模組中的函式生成大多數版本的 UUID。

MySQL 提供了一個 UUID 函式，它生成標準的版本 1 UUID。

當 UUID 用作主鍵時，版本 3、4 和 5 UUID 的隨機性以及 版本 1 和 2 UUID 內的欄位的排序可能會產生資料庫定位或效能問題。例如，2002 年 Jimmy Nilsson 報告說，當用作主鍵的版本 4 UUID 被修改為包含基於系統時間的非隨機字尾時，Microsoft SQL Server 的效能顯著提高。Nilsson 承認，這種所謂的「COMB」（組合時間和 GUID）方法使 UUID 非標準並且更有可能被複製，但 Nilsson 僅要求在應用程式中的唯一性。透過重新排序和編碼版本 1 和版本 2 的 UUID，將時間戳放在最前面，可以避免插入所造成的效能損失。

諸如 Laravel 這樣的部分網路框架支援「時間戳優先」的 UUID，可以將 UUID 有效儲存於索引資料庫中。這種 UUID 是版本 4 格式的 COMB UUID，但其中前 48 位元組成了一個時間戳，就像版本 1 的 UUID 一樣。其他基於 COMB UUID 概念的指定格式包括：

* **ULID**，其拋棄了用於表示版本 4 的 4 位元，並且預設使用 base32 編碼。
* UUID 版本 6 到 8 是三種 COMB UUID 格式的正式提議。

## 參見

* 全域唯一識別碼（GUID）

## 參考文獻