本文目录导读:

生成短链接的核心思路就是建立“短码”和“长链接”的映射关系,并进行重定向,下面从流程、算法、数据库设计和代码实现四个层面拆解。
核心流程(一次完整请求)
用户访问短链 http://a.cn/xYz9k
↓
PHP 解析 URL,取出短码 "xYz9k"
↓
查数据库/缓存:SELECT long_url FROM short_urls WHERE short_code = 'xYz9k'
↓
找到后,返回 301/302 重定向到长链接
↓
浏览器跳转到目标网站
短码生成算法(重点)
短码一般由 62个字符(0-9、a-z、A-Z)组成,长度通常 6~8 位。
随机字符串(简单但需防冲突)
function generateShortCode($length = 6) {
$chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$code = '';
for ($i = 0; $i < $length; $i++) {
$code .= $chars[random_int(0, 61)];
}
return $code;
}
缺点:需要查重,并发高时可能冲突。
自增ID + 进制转换(推荐,无冲突)
- 数据库插入一条记录,获得自增ID(10001)
- 将这个ID转换为62进制字符串,作为短码
function idToShortCode($id) {
$chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$code = '';
while ($id > 0) {
$code = $chars[$id % 62] . $code;
$id = intdiv($id, 62);
}
return str_pad($code, 6, '0', STR_PAD_LEFT); // 补位,保证长度
}
优点:绝对不冲突,还可以反向解码(由短码推出ID,便于统计)。
MD5/SHA1 截取(适合无需存储的短码)
$code = substr(md5($longUrl . time() . rand()), 0, 6);
缺点:碰撞概率高(6位62进制 = 568亿组合),需查重,且无法反向解码做统计。
数据库设计
CREATE TABLE short_urls (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
short_code VARCHAR(10) NOT NULL UNIQUE,
long_url TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
visit_count INT DEFAULT 0,
INDEX idx_code (short_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
PHP 完整实现
生成接口(接收长链接,返回短链接)
// generate.php
<?php
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$longUrl = $_POST['url'] ?? '';
if (!filter_var($longUrl, FILTER_VALIDATE_URL)) {
die('Invalid URL');
}
// 首先检查是否已存在
$stmt = $pdo->prepare("SELECT short_code FROM short_urls WHERE long_url = ?");
$stmt->execute([$longUrl]);
$exists = $stmt->fetchColumn();
if ($exists) {
echo "http://a.cn/{$exists}";
exit;
}
// 插入获取自增ID
$pdo->prepare("INSERT INTO short_urls (long_url) VALUES (?)")->execute([$longUrl]);
$id = $pdo->lastInsertId();
// ID转短码
$code = idToShortCode($id);
$pdo->prepare("UPDATE short_urls SET short_code = ? WHERE id = ?")->execute([$code, $id]);
echo "http://a.cn/{$code}";
访问重定向入口
// index.php (配合 .htaccess 将所有短链请求转发到这里)
<?php
$requestUri = $_SERVER['REQUEST_URI'];
$shortCode = ltrim($requestUri, '/'); // 取出短码
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$stmt = $pdo->prepare("SELECT long_url FROM short_urls WHERE short_code = ?");
$stmt->execute([$shortCode]);
$longUrl = $stmt->fetchColumn();
if ($longUrl) {
// 302 临时重定向(方便后期修改长链接);301 永久重定向有SEO优势
header("Location: " . $longUrl, true, 302);
exit;
} else {
http_response_code(404);
echo "链接不存在";
}
性能优化要点(进阶)
使用 Redis 缓存热点短码
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$longUrl = $redis->get("short:{$shortCode}");
if (!$longUrl) {
// 查数据库
$longUrl = queryDB($shortCode);
// 写入缓存,设置过期时间
$redis->setex("short:{$shortCode}", 86400, $longUrl);
}
header("Location: " . $longUrl);
使用异步/队列写入数据
高并发场景下,可以先写Redis再异步同步到MySQL。
防恶意刷接口
限制同一IP每天生成次数,或使用验证码。
生产环境注意事项
| 事项 | 说明 |
|---|---|
| 短码长度 | 建议 6~8 位,越短越利于复制,但碰撞率越高 |
| 重定向状态码 | 302 便于后期修改;如果确定永久不改用 301 |
| 自定义短码 | 增加允许用户自定义(需检查唯一) |
| 过期时间 | 非永久短链需加 expire_at 字段 |
| 安全过滤 | 防止 javascript: 等危险协议,需校验 http/https |
完整项目结构建议
/your-project/
├── index.php # 重定向入口
├── generate.php # 短链生成接口
├── .htaccess # 伪静态规则
├── config.php # 数据库配置
└── lib/
└── shortener.php # 核心类(生成+解析)
就是PHP生成短链接的完整思路,核心是ID→62进制编码保证无冲突,配合Redis缓存 + 数据库持久化即可支撑高并发场景。