自增ID转62进制是最可行起点,因其天然唯一、严格递增、可逆;而base64含特殊字符需编码,md5截取易碰撞且不可逆,36进制字符集窄、含混淆字符,真62进制需剔除0O1lI并手动实现除余逻辑与边界处理。

直接用自增 ID 转 62 进制是最可行、最可控的起点,别一上来就搞哈希或随机生成——容易撞库、难去重、没法保证唯一性,还绕不开数据库查重这一步。
为什么必须用自增 ID + 62 进制,而不是 base64 或 md5 截取
base64 含 +、/、=,进 URL 要 urlencode,破坏短码简洁性;md5 截取(如 substr(md5($url),0,6))看似简单,但碰撞概率随量级上升——10 万条链接里大概率出现重复,且无法反向还原,只能靠布隆过滤器兜底,徒增复杂度。而自增 ID 是天然全局唯一、严格递增、可逆的源头,62 进制只是它的“友好外壳”。
关键差异点:
-
base_convert($id, 10, 36)不支持自定义字符集,会跳过大小写区分,实际可用字符只剩 36 个,6 位仅能编码约 217 亿,且含易混淆字符0和O - 真 62 进制应剔除
0O1lI等视觉相似字符,常用精简表:'23456789abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ' - ID 为 0 时,循环取模逻辑不触发,必须显式返回
$chars[0],否则返回空字符串
idToShort() 函数怎么写才不出错
核心是模拟手算“除 k 取余”,但顺序要反——每次余数是低位,拼接后需 strrev();不能依赖 base_convert(),它不处理边界也不可控。
立即学习“PHP免费学习笔记(深入)”;
常见错误现象:
- 输入
id = 0,函数返回空,导致短码为空字符串,插入数据库失败或被截断 - 没做整除向下取整,PHP 中
$id / 62可能是浮点数,大 ID 下intval()或 (int) 强转会丢精度 - 字符表长度硬写 62,但实际
strlen($chars)是 58(剔除了 4 个易混字符),导致取模越界
正确写法示例:
function idToShort($id, $chars = '23456789abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ') {
$base = strlen($chars);
if ($id === 0) return $chars[0];
$short = '';
while ($id > 0) {
$short .= $chars[$id % $base];
$id = (int)($id / $base); // 必须 int cast,避免浮点误差
}
return strrev($short);
}shortToId() 解码时最容易忽略的三个检查点
这是 encode 的逆过程,但比 encode 更脆弱:一个非法字符、一次越界查找、一次位权计算偏差,ID 就全错了。
必须做的校验:
- 用
preg_match('/^[2-9a-zA-Z]{2,8}$/', $short)先过滤非法字符和长度,拒绝0O1lI-_.等一切非表内字符 - 查字符索引必须用
strpos($chars, $char),不是array_search()——前者快、大小写敏感、不依赖键名;返回false时立刻终止,不能参与后续累加 - 位权计算禁用
pow(62, $i),浮点数在 $i ≥ 10 时开始失准;改用整型累乘:$multiplier = 1; for ($i = strlen($short)-1; $i >= 0; $i--) { $id += strpos($chars, $short[$i]) * $multiplier; $multiplier *= $base; }
数据库设计里那个 UNIQUE 索引到底建在哪
不是建在 long_url 上,而是必须建在 short_code 上——因为跳转路由只认短码,查询条件是 WHERE short_code = ?。如果只对 long_url 加前缀索引(如 long_url(255)),那是为了防止重复提交相同长链接时插入冗余记录,属于写入优化,不影响读性能。
真正影响跳转速度的是 short_code 字段的唯一性与索引效率:
-
short_code类型建议用CHAR(8)而非VARCHAR,固定长度让 B+ 树索引更紧凑 - 必须加
UNIQUE约束,否则并发插入时可能生成相同短码,导致跳转错乱 - 不要省略
INDEX idx_short_code (short_code),哪怕字段已设为 UNIQUE——MySQL 的 UNIQUE 约束自带索引,但显式命名便于后期维护和排查
短码长度控制在 4–8 位之间最实际:太短(如 3 位)在 58 进制下仅支持约 19.5 万 ID,不够用;太长(如 12 位)又失去“短”的意义,且增加暴力遍历风险窗口。



















