视频平台、在线教育站点以及各类内容付费系统,几乎都会遇到同一个需求:把一个冗长的视频播放地址,转换成一串简短、便于传播的链接。直接暴露原始地址不仅体验差,还容易被爬虫遍历抓取资源。短链接的本质是维护一个短码与原始URL的映射关系,用户访问短码时由服务端完成跳转。本文以PHP为例,完整讲解视频播放短链接的生成思路与落地代码。

短链接的基本原理与数据表设计
短链接的核心逻辑非常朴素:原始长地址存入数据库,得到一个唯一ID,把这个ID转换成尽量短的字符串作为短码,访问短码时反查数据库拿到长地址并执行302跳转。之所以要做进制转换,是因为自增ID是十进制数字,位数增长快,而如果把ID转换成62进制(0-9、a-z、A-Z共62个字符),同样的数值可以用更少的字符表示。例如十进制的1000000000,转换成62进制只有6个字符左右。
设计数据表时,建议除了短码和原始地址外,再预留创建时间、过期时间、访问次数等字段,方便后续做统计和权限控制。下面是一张典型表结构:
CREATE TABLE `video_short_url` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增主键', `code` VARCHAR(10) NOT NULL DEFAULT '' COMMENT '短码', `url` VARCHAR(1000) NOT NULL COMMENT '原始视频播放地址', `expires_at` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '过期时间戳,0表示永久', `click_count` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '点击次数', `created_at` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '创建时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_code` (`code`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='视频短链接表';
把code字段设成唯一索引非常重要。短码冲突时插入会直接失败,程序可以捕获异常后重新生成,避免线上出现两个不同视频对应同一个短码的严重问题。url字段建议用VARCHAR(1000),因为视频地址经常拼接签名参数,长度可能远超预期。
基于自增ID与62进制转换的生成方案
这是最主流的做法。新建一条记录拿到自增ID,再把ID转成62进制短码。62进制的字符表顺序可以自定义,打乱顺序能增加一点猜测难度。转换过程就是不断对62取余数,把余数映射为字符,最后反转字符串:
<?php
class ShortUrl
{
// 62进制字符表,顺序可自行打乱
private static $chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
// 十进制ID转62进制短码
public static function encode($id)
{
$result = '';
$len = strlen(self::$chars);
while ($id > 0) {
$result .= self::$chars[$id % $len];
$id = intval($id / $len);
}
return strrev($result);
}
// 62进制短码转回十进制ID
public static function decode($code)
{
$id = 0;
$len = strlen(self::$chars);
for ($i = 0, $n = strlen($code); $i < $n; $i++) {
$pos = strpos(self::$chars, $code[$i]);
if ($pos === false) {
return 0; // 非法字符
}
$id = $id * $len + $pos;
}
return $id;
}
}
生成短链接的完整流程如下:先插入一条占位记录拿到ID,再更新code字段。为什么不直接用ID做短码?因为直接暴露自增ID等于告诉别人你的视频总量,而且十进制位数也偏长。示例代码:
<?php
// PDO连接数据库
$pdo = new PDO('mysql:host=127.0.0.1;dbname=test;charset=utf8mb4', 'root', '123456');
function createShortUrl(PDO $pdo, $videoUrl, $expiresAt = 0)
{
$stmt = $pdo->prepare('INSERT INTO video_short_url (url, expires_at, created_at) VALUES (?, ?, ?)');
$stmt->execute([$videoUrl, $expiresAt, time()]);
$id = $pdo->lastInsertId();
$code = ShortUrl::encode((int)$id);
$update = $pdo->prepare('UPDATE video_short_url SET code = ? WHERE id = ?');
$update->execute([$code, $id]);
return 'https://v.ipipp.com/s/' . $code;
}
// 示例:生成一个7天后过期的视频播放短链接
echo createShortUrl($pdo, 'https://cdn.ipipp.com/videos/2024/course-lesson-01.mp4?sign=abc123', time() + 7 * 86400);
这种方案的优点是短码长度可控、无冲突、可反解,通过decode方法甚至不用查库就能还原ID。缺点是依赖数据库自增ID,如果业务分库分表,ID不再连续,需要借助发号器或雪花算法来生成全局唯一ID。
短码跳转控制器的实现
生成了短码,还需要一个入口处理用户访问。以常见的路由规则/s/{code}为例,控制器接收短码,查询数据库校验有效期,然后执行跳转。视频场景下一般不要用302直接跳到视频文件本身,而是跳转到播放页面,播放页面再校验用户权限,否则短链接就成了资源直链的免费通道。
<?php
// 访问入口:https://v.ipipp.com/s/xxxxxx
$code = isset($_GET['code']) ? trim($_GET['code']) : '';
if ($code === '' || !preg_match('/^[0-9a-zA-Z]{1,10}$/', $code)) {
http_response_code(404);
exit('链接不存在');
}
$stmt = $pdo->prepare('SELECT * FROM video_short_url WHERE code = ? LIMIT 1');
$stmt->execute([$code]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$row) {
http_response_code(404);
exit('链接不存在');
}
// 校验有效期
if ($row['expires_at'] > 0 && $row['expires_at'] < time()) {
http_response_code(410);
exit('链接已过期');
}
// 累加访问次数(异步统计场景建议改投队列)
$pdo->prepare('UPDATE video_short_url SET click_count = click_count + 1 WHERE id = ?')
->execute([$row['id']]);
// 跳转到播放页面,短码作为参数传入
header('Location: /play.php?code=' . $code, true, 302);
exit;
注意代码中对短码做了正则校验,只允许字母数字,这是防止SQL注入和异常字符的必要手段,即使使用了预处理语句也建议保留。访问统计的UPDATE语句在高并发下会成为瓶颈,如果短链接的量级较大,建议把点击事件写入Redis的List或投递到消息队列,由后台任务批量落库。
其他生成方案对比与选型建议
除了自增ID加62进制,还有两种常见思路。第一种是随机字符串方案:随机生成指定长度的短码,查库确认不重复后插入。实现简单,不依赖ID连续,但随时间推移冲突率上升,需要处理重试逻辑。第二种是对URL做MD5或哈希运算后截取前几位,优点是完全无状态,同一地址天然去重,缺点是截取后的哈希存在碰撞可能,冲突处理反而更麻烦。
<?php
// 方案一:随机短码
function randomCode($length = 6)
{
$chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
return substr(str_shuffle(str_repeat($chars, 10)), 0, $length);
}
// 插入时捕获重复键异常并重试
function createRandomShortUrl(PDO $pdo, $url)
{
for ($i = 0; $i < 3; $i++) {
$code = randomCode(6);
try {
$stmt = $pdo->prepare('INSERT INTO video_short_url (code, url, created_at) VALUES (?, ?, ?)');
$stmt->execute([$code, $url, time()]);
return $code;
} catch (PDOException $e) {
// 23000为唯一键冲突,继续重试
if ($e->getCode() != 23000) {
throw $e;
}
}
}
throw new RuntimeException('短码生成失败');
}
三种方案各有取舍。中小型视频站点、短链接数量在千万级以内的,推荐自增ID加62进制,简单可靠且短码最短。已做分库分表、无法依赖单库自增ID的系统,可以用雪花ID替代后同样走62进制转换,或者改用随机短码。对去重有强需求的场景,比如同一视频地址只需一个短链,可以在表上给url加索引,插入前先查询,配合MD5前缀做快速比对。
最后提醒两个容易踩坑的点:一是视频短链接务必配合有效期和访问次数限制,付费视频尤其要校验用户登录态后再放行播放地址,否则短链接会被直接分享传播;二是短码跳转务必使用302或301状态码,不要在控制器里echo视频内容或使用file_get_contents转发,那样会占用PHP进程带宽,性能极差。遵循这些原则,一套稳定可扩展的视频短链接服务就搭建完成了。