news 2026/5/9 3:50:28

百亿级短链接系统设计:如何用 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/5/9 2:13:20

基于单片机的PID调节脉动真空灭菌器上位机远程监控设计

基于单片机的PID调节脉动真空灭菌器上位机远程监控设计概述 点击链接下载设计资料&#xff1a;https://download.csdn.net/download/qq_39020934/92091240 1.1 研究背景与设计意义 脉动真空灭菌器广泛应用于医疗器械、生物实验室以及制药行业&#xff0c;是保证器械和材料无菌…

作者头像 李华
网站建设 2026/5/9 2:54:13

每日一个C++知识点|异步编程

上篇文章说到C多线程的基础知识, 这篇文章主要说C多线程的另一个重要知识–异步 异步 那么什么是异步呢? 当程序执行一个耗时任务的时候, 主线程硬生生等待线程任务结束,不仅效率低, 还会让程序响应变得卡顿 这时候我们可以使用异步编程来解决这个问题,异步编程的核心就是非阻…

作者头像 李华
网站建设 2026/5/9 1:54:19

探索非线性电液伺服系统:基于ESO的反步滑模控制之旅

非线性电液伺服系统&#xff0c;基于ESO(扩张状态观测器)的反步滑模控制。 pdf教程matlab/simulink源程序。 s—函数搭建 1.通过扩展状态观测器估计速度、加速度和总扰动; 2.根据在线估计的系统模型&#xff0c;设计包含反步控制和滑模控制的控制率&#xff0c;对实际系统进行控…

作者头像 李华
网站建设 2026/5/9 1:54:31

详谈:解释器模式(二)

接上文。看到这个需求&#xff0c;我们很容易想到一种写法&#xff1a;将输入的字符串分割成单个字符&#xff0c;把数字字符通过switch-case转换为数字&#xff0c;再通过计算符判断是加法还是减法&#xff0c;对应做加、减计算&#xff0c;最后返回结果即可。计划的确可行&am…

作者头像 李华
网站建设 2026/5/9 2:51:52

Redis缓存三大问题详解:击穿、穿透与雪崩的解决方案

在使用 Redis 作为缓存层时&#xff0c;我们经常会遇到三个经典问题&#xff1a;缓存击穿、缓存穿透和缓存雪崩。这些问题可能导致系统性能下降甚至崩溃&#xff0c;本文将详细介绍这三个问题的原因和解决方案。一、&#x1f3af; 缓存击穿问题描述&#xff1a;某个热点 key 在…

作者头像 李华