技术栈:
- redis
- mysql
- laravel - 自行选择
转换方式
使用时间 + 序列号生成唯一值,然后使用10进制转62进制 我这里使用 Ymd + 序列号(0,1,2,3,4,5...)
// 获取日期 - 方便分表查询
$day = now()->format('Ymd'); // 20230919
// 获取序列号 - 使用redis - incr 原子性,防止序列号重复
$redisClient = Redis::connection()->client();
$serialNumber = $redisClient->incr('redis-key');
// 使用10进制转62进制 - 拼接十进制字符串:202309191
$shortLink = decimalToBase62($day . serialNumber);
echo $shortLink;
// 转换结果: DgrrT
十进制转62进制
function decimalToBase62($decimal) {
$base62 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
$base62_length = strlen($base62);
$result = '';
while ($decimal > 0) {
$remainder = $decimal % $base62_length;
$result .= $base62[$remainder];
$decimal = intdiv($decimal, $base62_length);
}
return strrev($result);
}
二进制转62进制
public function base62ToDecimal($number) {
$base62 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
$base62_length = strlen($base62);
$number = strrev($number);
$dec = 0;
for ($i = 0; $i < strlen($number); $i++) {
$digit = strpos($base62, $number[$i]);
$dec += $digit * pow($base62_length, $i);
}
return $dec;
}
举例
用户使用:www.php.net/manual/zh/l… 进行转换短链,我们就可以使用上面得到的字符串进行绑定
"DgrrT" => "https://www.php.net/manual/zh/language.basic-syntax.php"
可以根据自己业务逻辑进行分表存储,因为是按照日期获取到的随机串,我们可以很方便的进行分表操作。
用户访问我们的短链。
举个例子: www.baidu.com/DgrrT 我们要进行什么操作?
- 62进制转十进制,得到我们的分表表名称。
- 查询数据库,得到长连接。
- 301、302重定向
那么问题来了,所有的请求全部打到数据库,如果我们的功能是开放的,那么这样直接请求数据库肯定是灾难性的,我们可以在上面的基础上进行优化,添加各种判断
伪代码大法 - biubiubiu :1
// 1、62进制转10进制,判断日期
// 2、使用redis 布隆过滤器判断我们自己的库中是否存在这个key,不存在直接抛出异常或者其他操作
// 3、根据key获取到长连接
// 4、301、302重定向
现在这样我们把所有的数据全部存储到redis中,查询对应长连接,我们只需要查询redis就足够了、那么就有存在redis内存无限增大的问题。另外还有一些长期不使用长连接也存储到我们的redis,导致空间浪费。 继续优化: 我们可以根据我们的需求分配redis的内存大小,使用redis的内存淘汰策略(如何设置请自行百度),把不经常使用key淘汰掉即可(也就是冷数据),留下的全部都是经常使用的数据或者新增的数据(也就是热数据),热数据直接在redis中查询,冷数据因为不经常使用,直接去库中查询,查询到之后在写入到redis中
伪代码大法 - biubiubiu :2
// 1、62进制转10进制,判断日期
// 2、使用redis 布隆过滤器判断我们自己的库中是否存在这个key,不存在直接抛出异常或者其他操作
// 3、根据key获取到长连接,长连接存在,执行5
// 4、分表查询数据库
// 5、301、302重定向
这只是我自己的一点小见解,如果有其他的方案,请在评论区留言,我也去学习一下,哇咔咔