news 2026/4/15 16:49:42

百亿级短链接系统设计:如何用 Redis + MurmurHash 实现高性能的“短网址生成器”?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
百亿级短链接系统设计:如何用 Redis + MurmurHash 实现高性能的“短网址生成器”?

🔗 前言:面试官最爱问的“简单题”

“请设计一个短链接系统,像t.cn/AbCdEf这种。”
这道题在字节、阿里、腾讯的面试中出现的概率高达 80%。

很多初学者会不假思索地回答:“用 Redis 的INCR自增 ID,然后转成 62 进制不就行了?”
错!大错特错!

  1. 安全隐患:自增 ID 是连续的,黑客写个脚本t.cn/1,t.cn/2… 就能遍历你的所有数据,窃取商业机密。
  2. 性能瓶颈:严重依赖 Redis 单点计数器,难以水平扩展。

今天,我们来聊聊大厂主流的**“摘要算法生成法”**,利用MurmurHash + Base62,打造一个安全、无碰撞、可扩展的百亿级短链接系统。


🧠 核心原理:为什么是 MurmurHash?

要将一个长长的 URL 压缩成短字符串,本质上是一个Hash(摘要)过程。

1. 为什么不用 MD5?

MD5 生成的结果是 128 位(32 个字符),太长了!如果我们截取前 6 位,哈希冲突(Collision)的概率会爆炸式增长。

2. MurmurHash 的优势

Google 出品的MurmurHash算法,是目前非加密型 Hash 算法中的王者。

  • 速度快:比 MD5 快几十倍。
  • 雪崩效应好:输入微小的变化,输出巨大的差异。
  • 32 位输出:MurmurHash32 返回的是一个整数,非常适合后续处理。
3. Base62 编码 (压缩的核心)

我们需要把 MurmurHash 算出的整数(比如394820123)转换成更短的字符串。
使用Base62(a-z, A-Z, 0-9 共 62 个字符):

  • 626≈56862^6 \approx 568626568亿
  • 627≈3.562^7 \approx 3.56273.5万亿

结论:只需 6 到 7 位字符,就足够容纳全世界的网页!


⚔️ 核心难点:如何解决“哈希冲突”?

这是这道题的致命考点
虽然 MurmurHash 优秀,但在百亿量级下,冲突是必然的(抽屉原理)。即:
Hash(URL_A) == Hash(URL_B)

工业级解决方案:加盐双重探测

  1. 计算h = MurmurHash(LongURL)
  2. h转为 Base62 字符串,即ShortURL
  3. 查库校验
    • 去 Redis/DB 查这个ShortURL是否已存在。
    • 情况 A (不存在):完美,直接存入。
    • 情况 B (存在,且长链接一致):幂等,直接返回原短链。
    • 情况 C (存在,但长链接不一致)冲突爆发!
  4. 解决冲突
    • 给 LongURL 后面加一个特殊的“盐”(比如DUPLICATE)。
    • 重新执行第 1 步:Hash(LongURL + "DUPLICATE")
    • 直到找到一个不冲突的位置。

🏗️ 架构设计:读写分离与多级缓存

在海量数据下,数据库存不下,Redis 存太贵。我们需要合理的架构。

架构流程图:

访问短链_读链路
生成短链_写链路
1. 提交长链
2. MurmurHash计算
3. 布隆过滤器查重
未命中
命中
加盐重算
1. GET请求
2. 查本地缓存
3. 查分布式缓存
4. 查数据库
5. 回填缓存
6. 302 跳转
重定向服务
用户访问短链
JVM 缓存
Redis Cluster
读取 MySQL
短链生成服务
API 网关
Base62 编码
Bloom Filter
写入 MySQL 分片库
触发冲突解决策略
用户生成请求

关键设计点:

  1. 布隆过滤器 (Bloom Filter):在写入 DB 前,先用布隆过滤器判断短链是否存在。这能拦截 99% 的数据库查询,极大提升写入性能。
  2. 301 vs 302
    • 301 (永久重定向):浏览器会缓存,减轻服务器压力,但无法统计点击量
    • 302 (临时重定向):每次都请求服务器,适合做数据分析(大家一般都选这个)。

🛠️ 代码实战:Java 实现核心逻辑

引入 Google Guava 库来使用 MurmurHash。

<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>31.1-jre</version></dependency>

核心工具类:

importcom.google.common.hash.Hashing;importjava.nio.charset.StandardCharsets;publicclassShortLinkGenerator{// Base62 字符集privatestaticfinalStringBASE62="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";/** * 将 10 进制转 62 进制 */publicstaticStringtoBase62(longnum){StringBuildersb=newStringBuilder();while(num>0){inti=(int)(num%62);sb.append(BASE62.charAt(i));num/=62;}returnsb.reverse().toString();}/** * 生成短链接 */publicStringgenerate(StringlongUrl){// 1. 使用 MurmurHash32 生成 32 位整数// 注意:需处理负数,转为 32 位无符号长整型longhash32=Hashing.murmur3_32_fixed().hashString(longUrl,StandardCharsets.UTF_8).padToLong();// 2. 转 Base62StringshortCode=toBase62(hash32);// 3. 解决冲突逻辑 (模拟)while(isConflict(shortCode,longUrl)){// 加盐重算longUrl+="[SALT]";hash32=Hashing.murmur3_32_fixed().hashString(longUrl,StandardCharsets.UTF_8).padToLong();shortCode=toBase62(hash32);}returnshortCode;}// 模拟数据库查询privatebooleanisConflict(StringshortCode,StringlongUrl){// 实际逻辑:// String existUrl = redis.get(shortCode);// return existUrl != null && !existUrl.equals(longUrl);returnfalse;}}

📊 百亿级优化:分库分表

当数据量达到 100 亿时,单表肯定存不下。
如何分片?

  • 方案:直接用ShortURL做分片键 (Sharding Key)。
  • 逻辑Hash(ShortURL) % 1024,将数据分散到 1024 张表中。
  • 优势:读取时,直接根据短链算出在哪张表,不需要遍历所有库。

📝 总结

设计一个短链接系统,不仅仅是写代码,更是对算法选择、冲突处理、存储架构的综合考量。

  • 拒绝自增 ID:为了安全。
  • 拥抱 MurmurHash:为了速度和随机性。
  • 布隆过滤器:为了极致的写入性能。

掌握了这套方案,面试官问你 TinyURL 设计时,你就可以自信地“降维打击”了。


博主留言:
想获取包含布隆过滤器分库分表配置的完整 Spring Boot 工程源码吗?
在评论区回复“短链”,我发给你一份《百亿级短链系统企业级落地代码》,直接 CV 就能用!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/15 16:47:47

一文详解Java中Thread、ThreadGroup 和 ThreadLocal<T> 三者的区别和用途

01-Thread (线程)1.1 核心含义Thread是Java中表示和管理“线程”本⾝的类&#xff1b;⼀个Thread对象就对应着⼀条独⽴的执⾏路径1.2 主要作用并发执行&#xff1a;允许程序同时运⾏多个任务&#xff0c;提⾼资源利⽤率和响应速度 封装任务&#xff1a;将需要并发执⾏的代码封装…

作者头像 李华
网站建设 2026/4/13 0:36:59

【time-rs】time库 ComponentRange 错误类型详解(error/component_range.rs)

这是一个 Rust 时间库中的组件范围错误类型&#xff0c;用于表示时间组件&#xff08;如年、月、日、时、分、秒等&#xff09;值超出允许范围的情况。 1. 结构体定义 pub struct ComponentRange {pub(crate) name: &static str, // 组件名称pub(crate) minimum: i64…

作者头像 李华
网站建设 2026/4/11 9:34:12

Qt定时执行:槽函数并非必须

在Qt C中&#xff0c;定周期执行一个函数时&#xff0c;链接的函数不一定必须是槽函数&#xff0c;但具体取决于实现方式。以下是详细分析&#xff1a; 1. 使用QTimer 信号-槽机制&#xff08;需要槽函数&#xff09; 原理&#xff1a;QTimer的timeout()信号连接到目标对象的…

作者头像 李华
网站建设 2026/4/12 5:39:13

基于单片机的多功能LCD音乐播放器设计

基于单片机的多功能LCD音乐播放器设计概述 点击下载设计资料&#xff1a;https://download.csdn.net/download/m0_51061483/92081531 1.1 设计背景与研究意义 随着嵌入式系统技术和数字多媒体技术的不断发展&#xff0c;基于单片机的音频播放设备在教学实验、电子设计实践以及…

作者头像 李华
网站建设 2026/4/15 16:45:22

粒子群算法在风光储微电网优化调度中的应用:经济目标下的电源侧与负荷侧运行策略优化

基于粒子群算法的考虑需求侧响应的风光储微电网优化调度 考虑电源侧与负荷侧运行成本&#xff0c;以经济运行为目标函数&#xff0c;风电、光伏、储能出力、上级电网购电记忆可削减负荷为优化变量&#xff0c;并采用粒子群算法进行求解。1. 系统概述 本项目实现了一个基于多目标…

作者头像 李华
网站建设 2026/4/11 4:13:45

DAY11@浙大疏锦行

笔记&#xff1a;参数优化步骤&#xff1a;1.在调参前&#xff0c;先建立基线模型&#xff1a;- 使用**默认参数**训练模型- 记录性能指标作为**对比基准**- 后续调参效果以此为参照2.对参数进行定义1️⃣ 网格搜索 (GridSearchCV)- 需要定义参数的**固定列表**&#xff08;par…

作者头像 李华