怎样用 PHP 实现简单的短链接生成器_基于进制转换算法实现
很多人在实现短链接时都会遇到一个选择——到底用62进制还是16进制?这个问题看似基础,但背后确实藏了一些设计逻辑。短链接为什么用 62 进制而不是 16 进制原因是,16进制(0–9 + a–f)总共只有16个字符,生成的ID映射太短,很容易被暴力猜出原始URL。而62进制(0–9 + a–z +
很多人在实现短链接时都会遇到一个选择——到底用62进制还是16进制?这个问题看似基础,但背后确实藏了一些设计逻辑。

短链接为什么用 62 进制而不是 16 进制
原因是,16进制(0–9 + a–f)总共只有16个字符,生成的ID映射太短,很容易被暴力猜出原始URL。而62进制(0–9 + a–z + A–Z)提供了62个唯一字符,在同样长度下能编码的整数范围要大得多——比如6位62进制数可以表示大约568亿个ID,对大多数中小项目来说,长期使用都够用了。
关键不是“进制多高级”,而是要在实际字符集中避开容易混淆的那些,比如0和O、1和l。实践中常用的是精简版62进制表,像这样:'23456789abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'(去掉了0O1lI等)。
PHP 中怎么把自增 ID 转成短码(encode)
核心思路是模拟“除k取余”的过程,但不能直接用base_convert()——它不支持自定义字符集,也不处理0的边界情况。
实践中的做法是:
- 用循环对ID做取模和整除,每次取
$chars[floor($id % 62)],然后$id = floor($id / 62)。 - 当ID为0时需要单独返回第一个字符,否则循环不执行,即
if ($id === 0) return $chars[0];。 - 结果字符串要
strrev(),因为余数是从低位开始取的。 - 不要用递归实现,大ID(比如大于1e6)可能触发PHP栈溢出。
示例代码:
$chars = '23456789abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ';function idToShort($id, $chars) { if ($id === 0) return $chars[0]; $short = ''; while ($id > 0) { $short .= $chars[$id % 62]; $id = (int)($id / 62); } return strrev($short);}怎么把短码还原回 ID(decode)
这是encode的逆过程:从左到右遍历字符,在$chars中查索引,按位权累加。注意大小写敏感,strpos()比array_search()更快,且不依赖键名。
容易踩的坑:
- 输入含非法字符(如
-、_、空格)会导致strpos()返回false,后续计算直接崩掉——提前用preg_match('/^[2-9a-zA-Z]+$/', $short)校验一下就好。 - 没处理空字符串或超长串(比如超过10位),可能会隐式转换出错——建议限制长度为2–8位。
- 用
pow(62, $i)计算位权,浮点误差在高位时会导致ID偏移——改用整型累乘:$multiplier *= 62。
示例代码:
function shortToId($short, $chars) { if (!preg_match('/^[2-9a-zA-Z]{2,8}$/', $short)) return false; $id = 0; $multiplier = 1; for ($i = strlen($short) - 1; $i >= 0; $i--) { $pos = strpos($chars, $short[$i]); if ($pos === false) return false; $id += $pos * $multiplier; $multiplier *= 62; } return $id;}数据库怎么存、怎么防重复和并发冲突
短码本质上是ID的“别名”,不是随机字符串,所以不能靠uniqid()或random_bytes()生成——那样无法反解,也丧失了映射的确定性。
必须基于主键ID编码,因此流程是:插入原始URL → 获取自增ID → encode成短码 → 更新该记录的short_code字段。
这里有几个常见的坑:
- 不加事务,高并发下两个请求可能拿到相同ID并生成相同短码——必须用
INSERT ... ON DUPLICATE KEY UPDATE或先SELECT FOR UPDATE。 - 短码字段没建唯一索引,重复插入失败后没有兜底重试——
short_code列必须加UNIQUE索引。 - 用MyISAM引擎,不支持行锁——换成InnoDB,配合事务和
INSERT ... SELECT来避免竞态。
真正考验功夫的往往不是算法本身,而是确保“ID → 短码”整个过程幂等,并且在数据库层面不允许出现多个URL映射到同一个短码的情况。

































