短链系统设计

221 阅读4分钟

短链系统设计

短链有哪些优势,系统又该如何设计,类似的文章太多,不重复赘述。

可以参考高性能短链设计, 本文就其中的一些点探讨一下。

一、流程

  • 数据量大

image-20250107191745-2pjhomd.png

  • 数据量小

    布隆过滤查询是否存在,可改为直接数据库索引查。也可以直接写,看唯一索引是否冲突

二、核心代码示例

这里借用了Guava包封装的MurmurHash方法。

import com.google.common.hash.Hashing;
import java.nio.charset.StandardCharsets;

/**
 * 短链设计
 */
public class MurmurHashToBase62 {
    private static final String BASE62_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

    public static void main(String[] args) {
        // 输入长链接
        String longUrl = "https://example.com/some/very/long/url/for/testing";
    
        // 计算 32 位 MurmurHash
        int hash32 = Hashing.murmur3_32().hashString(longUrl, StandardCharsets.UTF_8).asInt();
        // MurmurHash 32-bit (Decimal): -1888812516
        System.out.println("MurmurHash 32-bit (Decimal): " + hash32);

        // 转换为正数(避免负值影响进制转换)
        long unsignedHash32 = hash32 & 0xFFFFFFFFL;

        // 转换为 62 进制
        String base62Hash = convertToBase62(unsignedHash32);
        // MurmurHash 32-bit (Base62): 2cpym4
        System.out.println("MurmurHash 32-bit (Base62): " + base62Hash);
    }

    // 将数字转换为 62 进制字符串
    private static String convertToBase62(long value) {
        StringBuilder base62 = new StringBuilder();
        while (value > 0) {
            int index = (int) (value % 62);
            base62.append(BASE62_CHARSET.charAt(index));
            value /= 62;
        }
        // 返回结果(注意:需要反转顺序)
        return base62.reverse().toString();
    }

}

三、一些关键点

3.1 如何选择多少长度短链
  1. 6位,可表示568亿个地址
626=56,800,235,58462^6 = 56,800,235,584
  1. 4位,可表示1477万个地址

  2. 如何固定长度( 有利于辨识度 )

    // 限制哈希值范围到 62^6 以内
    long limit = (long) Math.pow(62, 6);
    long fixedRangeHash = unsignedHash32 % limit;
    
    // 转换为 62 进制
    String base62Hash = convertToBase62(fixedRangeHash);
    
    // 如果长度不足 6 位,前补零或其他字符
    while (base62Hash.length() < 6) {
        base62Hash = "0" + base62Hash;
    }
    

    因此,如果想采用4位的短链,直接截取转62进制后的前4位即可;如果不够,自行补足 ‍

3.2 MurmurHash算法

MurmurHash 是一种非加密型哈希函数,与其它流行的哈希函数相比,对于规律性较强的key,MurmurHash的随机分布特征表现更良好。即,冲突概率更低,更适用于于URL这种有着强规律的数据。

  • 原理

    MurmurHash算法基于混合函数的设计思想,它将输入数据分为若干段,并对每一段执行一个非线性函数(根据数据类型和大小动态调整),将结果合并为一个最终的哈希值。在MurmurHash中,这种分段和混合函数的选择是关键。通过对输入数据进行适当的混合和混淆,MurmurHash能够产生更好的哈希分布,降低碰撞概率。

3.3 如何解决hash冲突

原地址添加自定义参数,然后重新hash

3.4 301 和 302 重定向的区别
  • 301,代表 永久重定向,也就是说第一次请求拿到长链接后,下次浏览器再去请求短链的话,不会向短网址服务器请求了,而是直接从浏览器的缓存里拿,这样在 server 层面就无法获取到短网址的点击数了,如果这个链接刚好是某个活动的链接,也就无法分析此活动的效果。所以我们一般不采用 301。
  • 302,代表 临时重定向,也就是说每次去请求短链都会去请求短网址服务器(除非响应中用 Cache-Control 或 Expired 暗示浏览器缓存),这样就便于 server 统计点击数

总结: 是采用301还是302,看系统设计对于短链跳转到定位,看性能优先还是数据统计优先。

四、其他方式

方案: 短链前缀+不重复自增ID

  1. 随机UUID+各类识别码等,不如直接长链hash
  2. Mysql自增主键,小规模可以玩
  3. Redis自增
  4. Snowflake,但是雪花算法生成ID是64位,通过62进制转换,至少是11位;截断的话,增大整体冲突概率
  5. 利用其他自增ID生成工具,如tinyId等

后话:
如果只是小公司的零散需求,也不考虑统计数据之类的。那么部署一个FTP,用nginx静态代理目录就行了,不需要再去整一堆服务。链接名自己定,反正就那么几个文件。