首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 產生唯一識別碼最佳實踐:UUID、ULID、雜湊與隨機數比較

     編輯:PHP 相關知識

常見需求場景

  • 資料庫主鍵 (分散式系統避免衝突)
  • 分散式追蹤 ID (Request ID, Trace ID)
  • 暫存檔名、快取鍵、Session ID
  • API 幂等性鍵 (Idempotency Key)
  • 安全 Token (CSRF, API Key, 重設密碼)

方案對比總覽

方案長度有序性碰撞機率可預測性適用場景
UUID v4 (Random)36 字元無極低 (122-bit 隨機)不可預測通用唯一 ID、主鍵
UUID v7 (Time-ordered)36 字元毫秒級有序極低部分可預測 (時間)資料庫主鍵、索引友善
ULID26 字元毫秒級有序極低 (128-bit)部分可預測URL 友善、主鍵、日誌排序
nanoid21 字元 (可調)無可調 (預設 ~149-bit)不可預測短 ID、URL 友善、前端生成
SHA-256 雜湊64 字元無極低確定性 (同輸入同輸出)內容定址、去重、驗證
random_bytes + base64可調無可調不可預測高安全 Token、加密金鑰
時間戳+隨機數 (原文做法)40 字元 (SHA1)秒級有序中 (依賴隨機源品質)部分可預測簡單場景、舊系統相容

現代 PHP 實作範例 (PHP 7.1+)

1. UUID v4 (標準隨機)

<?php
// 使用 ramsey/uuid (Composer: composer require ramsey/uuid)
use Ramsey\Uuid\Uuid;
$id = Uuid::uuid4()->toString(); // "550e8400-e29b-41d4-a716-446655440000"

// 原生實作 (RFC 4122 v4)
function uuidV4(): string
{
    $bytes = random_bytes(16);
    // 設定版本位元 (4) 與變體位元 (10x)
    $bytes[6] = chr((ord($bytes[6]) & 0x0f) | 0x40); // version 4
    $bytes[8] = chr((ord($bytes[8]) & 0x3f) | 0x80); // variant 10x
    return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($bytes), 4));
}

2. UUID v7 / ULID (時間有序,索引友善)

<?php
// ULID 實作 (Crockford's Base32, 26 chars)
function ulid(): string
{
    $ms = (int)(microtime(true) * 1000);
    $time = str_pad(base_convert($ms, 10, 32), 10, '0', STR_PAD_LEFT); // 10 chars
    $rand = '';
    $bytes = random_bytes(10); // 80 bits
    foreach (str_split(bin2hex($bytes), 2) as $b) {
        $rand .= '0123456789ABCDEFGHJKMNPQRSTVWXYZ'[hexdec($b) & 0x1F];
    }
    return $time . $rand; // 26 chars, e.g. 01ARZ3NDEKTSV4RRFFQ69G5FAV
}

// UUID v7 (RFC 9562 草案,時間前 48 bits + 版本 7 + 亂數)
function uuidV7(): string
{
    $ms = (int)(microtime(true) * 1000);
    $timeBytes = pack('J', $ms); // 6 bytes (48 bits), big-endian
    $randBytes = random_bytes(10);
    $bytes = substr($timeBytes, 2) . $randBytes; // 16 bytes
    $bytes[6] = chr((ord($bytes[6]) & 0x0f) | 0x70); // version 7
    $bytes[8] = chr((ord($bytes[8]) & 0x3f) | 0x80); // variant
    return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($bytes), 4));
}

3. 高安全隨機 Token (API Key, CSRF, 重設密碼)

<?php
function secureToken(int $bytes = 32): string
{
    // URL-safe Base64, 無填充
    return rtrim(strtr(base64_encode(random_bytes($bytes)), '+/', '-_'), '=');
}

// 使用
$apiKey = 'sk_' . secureToken(24);  // sk_xxxxx... (43 chars)
$csrf   = secureToken(32);          // 43 chars
$reset  = secureToken(16);          // 22 chars

4. 內容定址雜湊 (檔案去重、快取鍵)

<?php
function contentHash(string $data, string $algo = 'sha256'): string
{
    return hash($algo, $data);
}

// 檔案雜湊 (串流處理大檔案)
function fileHash(string $path, string $algo = 'sha256'): string
{
    return hash_file($algo, $path);
}

資料庫主鍵選型建議

  1. 單機/小規模:BIGINT AUTO_INCREMENT 最簡單高效。
  2. 分散式/微服務:UUID v7 或 ULID,時間有序減少 B+ 樹分頁分裂,寫入效能優於隨機 UUID。
  3. 公開 API 資源 ID:ULID 或 nanoid,短、URL-safe、不洩露資料量。
  4. 避免:原文 sha1(time().rand()) — 熵來源弱、長度固定 40 字元、無序、SHA1 已棄用。

安全性關鍵點

  • 永遠使用 random_bytes() / random_int() (PHP 7+ CSPRNG),禁用 rand()、mt_rand()、uniqid()、md5(time()) 產生安全 Token。
  • Token 長度:最少 128-bit (16 bytes) 熵,即 22 字元 URL-safe Base64 或 32 字元 Hex。
  • 時序攻擊防護:驗證 Token 時使用 hash_equals() 恆時比較。
  • 儲存雜湊而非明文:資料庫儲存 hash('sha256', $token),驗證時比對雜湊。

效能測試參考 (PHP 8.2, 100萬次)

uuidV4 (ramsey)      : ~0.45 秒
uuidV4 (native)      : ~0.18 秒
ulid()               : ~0.12 秒
uuidV7()             : ~0.15 秒
secureToken(32)      : ~0.10 秒
sha1(time().rand())  : ~0.08 秒 (但不安全!)
random_bytes(16)     : ~0.06 秒 (最基礎操作)

總結

PHP 產生唯一 ID 已進入標準化、高效能、安全優先新時代。核心建議:

  1. 主鍵/排序需求 → UUID v7 或 ULID (時間有序、索引友善)
  2. 通用唯一 ID → UUID v4 (標準、生態完善)
  3. 短 ID/URL 友善 → ULID 或 nanoid
  4. 安全 Token → random_bytes() + Base64URL (CSPRNG、恆時驗證、儲存雜湊)
  5. 內容定址 → SHA-256 / BLAKE3

棄用 sha1(time().rand()) 等弱熵、定長、無序舊寫法。引入 ramsey/uuid、ulid/php-ulid、hidehalo/nanoid-php 等成熟套件,專注業務邏輯而非重造輪子。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved