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

PHP 生成唯一標識符 (ID/Token/GUID) 完整方案:uniqid、隨機數、UUID、雪花算法

     編輯:PHP 相關知識

PHP 生成唯一標識符 (ID/Token/GUID) 完整方案:uniqid、隨機數、UUID、雪花算法

業務中無處不在的「唯一標識」:訂單號、邀請碼、API Token、分佈式主鍵、幂等鍵、追蹤 ID。PHP 提供從內建函數到擴展庫、從單機到分佈式的完整生成方案。本文按安全性、有序性、長度、生成速度、碰撞概率五大維度對比主流方案,給出選型決策樹與生產級代碼。

方案對比總覽

方案長度有序性安全性碰撞率適用場景
uniqid()13/23 字符微秒級遞增低 (可預測)高併發極高臨時文件名、非關鍵業務
md5(uniqid())32 字符無序低極低一般唯一鍵、緩存 Key
random_bytes(16)32/24 字符無序高 (CSPRNG)理論零Token、Session ID、API Key
UUID v4 (隨機)36 字符無序高理論零分佈式主鍵、跨系統標識
UUID v1 (時間+MAC)36 字符粗略有序中 (洩露 MAC)極低需粗略排序的分佈式 ID
ULID26 字符毫秒級有序高極低現代替代 UUID v1,索引友好
雪花算法 (Snowflake)19 位數字毫秒級有序中 (需保護機器 ID)零 (單調遞增)高併發分佈式主鍵、MySQL 主鍵
NanoID21 字符 (可配置)無序高可調短 ID、URL 友好、前端傳遞

方案一:內建函數快速生成 (非安全場景)


// uniqid(): 微秒時間戳 + 微秒計數器
echo uniqid();           // 65f8a3b2c1d4e (13 字符)
echo uniqid('ord_');     // ord_65f8a3b2c1d4e (帶前綴)
echo uniqid('', true);   // 65f8a3b2c1d4e.12345678 (23 字符, 更多熵)

// 組合增強 (仍非安全)
echo md5(uniqid('', true));        // 32 字符十六進制
echo bin2hex(random_bytes(16));    // 32 字符,**推薦替代上行**
echo base64_encode(random_bytes(16)); // 24 字符 URL-safe (需替換 +/=)

方案二:加密級隨機 Token (安全場景首選)


function generateSecureToken(int $bytes = 32): string
{
    // PHP 7+ random_bytes() 使用系統 CSPRNG (Linux: getrandom, Windows: BCryptGenRandom)
    return bin2hex(random_bytes($bytes)); // 64 字符十六進制
}

// URL-safe Base64 (無 + / =)
function generateUrlSafeToken(int $bytes = 24): string
{
    return rtrim(strtr(base64_encode(random_bytes($bytes)), '+/', '-_'), '=');
}

// 使用示例
$apiKey = 'sk_' . generateUrlSafeToken(32); // sk_xYz7... (43 字符)
$csrfToken = generateSecureToken(32);
$sessionId = generateUrlSafeToken(16);

方案三:UUID (RFC 4122 標準)

純 PHP 實現 UUID v4 (隨機)


function uuid_v4(): string
{
    $bytes = random_bytes(16);
    // 設置版本位 (4) 和變體位 (10xx)
    $bytes[6] = chr((ord($bytes[6]) & 0x0F) | 0x40); // version 4
    $bytes[8] = chr((ord($bytes[8]) & 0x3F) | 0x80); // variant 10
    return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($bytes), 4));
}

// 使用 ramsey/uuid 庫 (生產推薦,Composer: ramsey/uuid)
use Ramsey\Uuid\Uuid;
$uuid = Uuid::uuid4()->toString(); // 標準 UUID v4
$uuid1 = Uuid::uuid1()->toString(); // 基於時間+MAC (需開啟 MAC 獲取)
$uuid6 = Uuid::uuid6()->toString(); // 新標準:時間有序 + 隨機
$uuid7 = Uuid::uuid7()->toString(); // Unix 毫秒時間戳 + 隨機 (最推薦用於 DB 主鍵)

方案四:ULID (Universally Unique Lexicographically Sortable Identifier)

26 字符、Base32 編碼、48 位毫秒時間戳 + 80 位隨機、天然按字典序排序 = 時間序,完美適配資料庫聚簇索引。


// 純 PHP 實現 (需 PHP 7+)
function ulid(): string
{
    $time = (int)(microtime(true) * 1000); // 毫秒時間戳
    $timeBytes = pack('J', $time); // 6 字節大端 (僅取低 48 位)
    $timeBytes = substr($timeBytes, -6); // 保證 6 字節
    $random = random_bytes(10); // 10 字節 = 80 位
    $binary = $timeBytes . $random;
    // Crockford's Base32 編碼 (0-9 A-Z 除 I L O U)
    $encode = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
    $ulid = '';
    for ($i = 0; $i < 16; $i++) {
        $byte = ord($binary[$i]);
        $ulid .= $encode[$byte >> 3 & 0x1F];
        $ulid .= $encode[($byte << 2 & 0x1C) | (ord($binary[$i+1] ?? 0) >> 6 & 0x03)];
    }
    return substr($ulid, 0, 26);
}

// 生產推薦:robinvdvleuten/ulid-php (Composer)
use Ulid\Ulid;
$ulid = Ulid::generate()->toString(); // 01ARZ3NDEKTSV4RRFFQ69G5FAV

方案五:雪花算法 (Snowflake) - 分佈式主鍵王者

64 位長整數:1bit 符號位 | 41bit 毫秒時間戳 (69年) | 10bit 機器/工作進程 ID (1024) | 12bit 序列號 (4096/ms)。
優勢:**單調遞增、無碰撞、極短 (19 位數字)、MySQL BIGINT 完美適配、索引性能極佳**。


// 純 PHP 實現 (單進程/單機演示,分佈式需引入 Redis/ZK 分配 workerId)
class Snowflake
{
    private int $workerId;
    private int $sequence = 0;
    private int $lastTimestamp = -1;
    private const EPOCH = 1609459200000; // 2021-01-01 00:00:00 UTC
    private const WORKER_ID_BITS = 10;
    private const SEQUENCE_BITS = 12;
    private const MAX_WORKER_ID = -1 ^ (-1 << self::WORKER_ID_BITS); // 1023
    private const MAX_SEQUENCE = -1 ^ (-1 << self::SEQUENCE_BITS);   // 4095

    public function __construct(int $workerId)
    {
        if ($workerId < 0 || $workerId > self::MAX_WORKER_ID) {
            throw new InvalidArgumentException("WorkerId must be 0-1023");
        }
        $this->workerId = $workerId;
    }

    public function nextId(): int
    {
        $timestamp = $this->timeGen();
        if ($timestamp < $this->lastTimestamp) {
            throw new RuntimeException("Clock moved backwards");
        }
        if ($timestamp === $this->lastTimestamp) {
            $this->sequence = ($this->sequence + 1) & self::MAX_SEQUENCE;
            if ($this->sequence === 0) {
                $timestamp = $this->tilNextMillis($this->lastTimestamp);
            }
        } else {
            $this->sequence = 0;
        }
        $this->lastTimestamp = $timestamp;
        return (($timestamp - self::EPOCH) << (self::WORKER_ID_BITS + self::SEQUENCE_BITS))
             | ($this->workerId << self::SEQUENCE_BITS)
             | $this->sequence;
    }

    private function timeGen(): int { return (int)(microtime(true) * 1000); }
    private function tilNextMillis(int $last): int {
        $ts = $this->timeGen();
        while ($ts <= $last) $ts = $this->timeGen();
        return $ts;
    }
}

// 使用
$sf = new Snowflake(1); // workerId=1
echo $sf->nextId(); // 1348274923847654401 (19 位數字)
> 🏭 生產級部署:使用 baidu/uid-generator (Java) 或 go-snowflake (Go) 作為獨立 ID 服務,PHP 通過 gRPC/HTTP 獲取 ID 批次,避免 PHP 進程間 workerId 衝突、時鐘回撥、單點故障。

方案六:NanoID - 短小精悍、URL 友好


// 隨機字母表 (默認 URL-safe)
function nanoid(int $size = 21, string $alphabet = '_-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'): string
{
    $bytes = random_bytes($size);
    $mask = (1 << (int)ceil(log(strlen($alphabet), 2))) - 1;
    $id = '';
    for ($i = 0; $i < $size; $i++) {
        $byte = ord($bytes[$i]) & $mask;
        if ($byte < strlen($alphabet)) {
            $id .= $alphabet[$byte];
        } else {
            $i--; // 重試
        }
    }
    return $id;
}

// 生產推薦:hidehalo/nanoid-php
use Hidehalo\Nanoid\Client;
$nanoid = (new Client())->generateId(); // V1StGXR8_Z5jdHi6B-myT (21 字符)

選型決策樹 (從上往下匹配)

  1. 🔐 需加密安全性 (Token、Key、Session) → random_bytes() / generateSecureToken()
  2. 🗄️ 分佈式資料庫主鍵 → Snowflake (長整數) 或 UUID v7/ULID (字符串有序)
  3. 🌐 跨系統/微服務追蹤 ID → UUID v4/v7 (標準化、工具鏈完善)
  4. 🔗 短連結/邀請碼/前端傳遞 → NanoID (21 字符) 或 base64_encode(random_bytes(12))
  5. 📁 臨時文件/緩存 Key/非關鍵去重 → uniqid('', true) / md5(uniqid())
  6. 📦 現有系統遷移 → 保持原格式,新系統採用上述現代方案。

避坑指南

  • ❌ 禁用 md5(uniqid()) 生成 Token:uniqid() 可預測,MD5 無加密強度,彩虹表可逆向。
  • ❌ 禁用 mt_rand()/rand() 生成任何安全標識:非 CSPRNG,種子可推導。
  • ✅ UUID 存儲用 CHAR(36) 或 BINARY(16):BINARY(16) 節省 50% 空間且索引更快 (需應用層 pack/unpack)。
  • ✅ Snowflake ID 用 BIGINT UNSIGNED:MySQL 8.0+ 支援隱式主鍵優化。
  • ✅ 分佈式環境務必解決 workerId 分配、時鐘同步、服務發現:單機測試代碼**不可直接上生產**。

唯一標識生成看似簡單,實則關繫安全性、資料庫性能、分佈式一致性、業務可追溯性。根據業務屬性選擇合適方案,並在架構層面統一規範 (如「所有新表主鍵採用 Snowflake BIGINT」、「所有對外 Token 採用 URL-safe Base64」),才能避免未來遷移重構的巨大代價。

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