news 2026/7/17 4:56:40

百亿级短链接系统设计:如何用 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/7/16 17:04:54

大梵公考:国考省考每一年的岗位一样吗?

国考省考每一年的岗位是不一样的&#xff0c;每年岗位都会重新制定&#xff0c;具体变化较大。主要变化原因&#xff1a;①编制调整各单位根据空编情况、业务需求申报招录计划&#xff0c;每年可用编制数量和岗位结构都可能调整。②政策导向变化如近年政策向基层、乡村振兴、紧…

作者头像 李华
网站建设 2026/7/16 17:04:54

大梵公考:国考和省考二选一怎么选?

国考与省考“二选一”怎么选&#xff1f;具体要看个人情况和职业规划。一、岗位性质对比国考&#xff1a;中央部委及其直属机构(如税务、海关、统计、铁路公安等)&#xff0c;多为垂直管理&#xff0c;系统内流动性强。省考&#xff1a;省、市、县、乡四级地方机关(如政府办、发…

作者头像 李华
网站建设 2026/7/16 17:04:54

Java中如何检测死锁?如何预防和避免线程死锁?

Java死锁实战指南&#xff1a;从检测到预防的完整解决方案 在并发编程中&#xff0c;死锁如同一个隐形的陷阱&#xff0c;随时可能让高性能应用陷入瘫痪。当多个线程相互等待对方释放锁时&#xff0c;程序便会永久停滞。 本文将提供一套完整的死锁解决方案&#xff1a;首先介绍…

作者头像 李华
网站建设 2026/7/16 17:04:54

Day32 类的定义和方法

题目 1&#xff1a;定义圆&#xff08;Circle&#xff09;类 # 示例运行 circle Circle(5) print(f"半径: {circle.radius}") print(f"面积: {circle.calculate_area()}") print(f"周长: {circle.calculate_circumference()}") 题…

作者头像 李华
网站建设 2026/7/16 5:07:36

自动化测试-关键数据记录(日志+截图+页面源码)

文章目录1. 自动化测试中的关键数据&#xff1f;2. 配置日志3. 截图4. pagesource源码记录总结✨✨✨学习的道路很枯燥&#xff0c;希望我们能并肩走下来&#xff01; 编程真是一件很奇妙的东西。你只是浅尝辄止&#xff0c;那么只会觉得枯燥乏味&#xff0c;像对待任务似的应…

作者头像 李华