短網址服務(如 bit.ly、t.cn)的核心是將長 URL 映射為短代碼。本文介紹一種基於 CRC32 哈希 + 62 進制編碼 的無數據庫生成算法,適合無需持久化映射、容許極低碰撞率的場景。
crc32() 返回有符號整數,用 sprintf('%u') 轉為無符號字符串。0-9a-zA-Z 共 62 個字符,對數值反覆取模 62、除以 62,將餘數映射為字符,直到商為 0。| 餘數範圍 | 映射字符 | ASCII 計算 |
|---|---|---|
| 0 ~ 9 | '0' ~ '9' | chr($mod + 48) |
| 10 ~ 35 | 'A' ~ 'Z' | chr($mod + 55) (10+55=65='A') |
| 36 ~ 61 | 'a' ~ 'z' | chr($mod + 61) (36+61=97='a') |
<?php
/**
* 生成短網址代碼
* @param string $url 原始長網址
* @return string 短代碼(如 "a3f9K")
*/
function dwz($url) {
// 1. CRC32 哈希並轉為無符號數字字符串
$code = sprintf('%u', crc32($url));
$surl = '';
// 2. 循環轉 62 進制
while ($code > 0) {
$mod = $code % 62;
if ($mod > 9 && $mod <= 35) {
$mod = chr($mod + 55); // A-Z
} elseif ($mod > 35) {
$mod = chr($mod + 61); // a-z
}
// 0-9 直接作為字符
$surl .= $mod;
$code = floor($code / 62);
}
// 3. 反轉字符串(因為是從低位到高位產生)
return strrev($surl);
}
// 測試
$urls = [
'https://www.example.com/very/long/url/with/parameters?foo=bar&baz=qux',
'https://github.com/user/repo/issues/12345',
'https://localhost:8080/api/v1/users?page=1&size=20'
];
foreach ($urls as $url) {
echo "原網址: $url\n";
echo "短代碼: " . dwz($url) . "\n";
echo "短網址: https://short.ly/" . dwz($url) . "\n\n";
}
?>
| 維度 | 評價 | 說明 |
|---|---|---|
| 是否需數據庫 | 不需要 | 純算法生成,無狀態,極簡部署 |
| 碰撞概率 | 極低但存在 | CRC32 32位空間約 42 億,生日悖論下 ~7.7 萬條數據有 50% 碰撞概率 |
| 可逆性 | 不可逆 | 無法從短代碼反推原 URL,需數據庫存儲映射才能跳轉 |
| 短碼長度 | 1 ~ 6 位 | 最大 62^6 ≈ 568 億組合,足夠大多數場景 |
| 適用場景 | 內部臨時分享、無狀態服務、日誌標記 | 不適用於公開短鏈服務(需持久化、防掃描、統計分析) |
若需構建正式短鏈服務,建議採用 「隨機字符串 + 數據庫映射」 模式:
random_bytes + Base62),檢查數據庫唯一性。short_code -> long_url,建立索引。<?php
const BASE62_CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
function base62_encode($num) {
if ($num === 0) return '0';
$result = '';
while ($num > 0) {
$result = BASE62_CHARS[$num % 62] . $result;
$num = intdiv($num, 62);
}
return $result;
}
function base62_decode($str) {
$num = 0;
$len = strlen($str);
for ($i = 0; $i < $len; $i++) {
$num = $num * 62 + strpos(BASE62_CHARS, $str[$i]);
}
return $num;
}
// 通用短碼生成:隨機數 + 編碼
function generate_short_code($length = 6) {
$bytes = random_bytes($length);
$num = 0;
foreach ($bytes as $byte) {
$num = ($num << 8) | $byte;
}
return base62_encode($num);
}
?>
CRC32+Base62 算法代碼簡潔、無依賴,適合快速原型、內部工具、日誌標記等輕量場景。構建對外服務時,請採用數據庫映射架構,確保唯一性、可管理性與擴展性。