尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

用 MurmurHash + Base62 生成短链接

用 MurmurHash + Base62 生成短链接
📅 发布时间:2026/6/19 17:40:30
短链接? 你有没有遇到过这种情况? 想在朋友圈分享一个活动链接,结果一粘贴——好家伙,一长串参数占了半屏,一看就烦,这时候,短链接的好处就体现出来了——简洁

短链接?

你有没有遇到过这种情况?

想在朋友圈分享一个链接,结果一粘贴——好家伙,一长串参数,占了半屏,还带一堆 ?utm_source=xxx&ref=yyy…… 别人一看就烦,自己都懒得点。更别说在短信、海报、二维码等空间有限的场景下了。

这时候,就需要一个短链接,比如把:

https://example.com/article?id=12345&source=wechat&utm_campaign=spring_sale

变成

https://ex.co/aB3k9

这类短链接简洁美观,易于传播并且可隐藏原始逻辑,用起来还是挺方便的。

哈希 + 编码 = 短码

要生成短链接,关键在于将任意长度的原始 URL 映射为一个固定长度、唯一且紧凑的字符串标识符(即“短码”)。

这里采用两步法:

第一步:哈希

使用非加密型哈希函数(如 MurmurHash)将原始 URL 转换为一个固定长度的整数(通常是 32 位)。
为什么不用 MD5 或 SHA?因为它们输出太长(MD5 是 32 位十六进制字符串),而我们需要的是短

MurmurHash 的优势

  • 高性能:计算速度快,适合高并发场景
  • 均匀分布:冲突率低,保证不同 URL 生成不同哈希值
  • 固定种子:Guava 提供的 murmur3_32_fixed() 使用固定种子,确保跨 JVM、跨机器结果一致
  • 非加密:不用于安全场景,正适合做 ID 生成

第二步:编码

将哈希得到的整数(可能为负数)转换为Base62 字符串。

Base62 是什么?

  • 字符集:0–9(10个) + A–Z(26个) + a–z(26个) = 共 62 个字符
  • 优点:URL 安全(不含 +, /, = 等特殊字符),可直接拼接到域名后
  • 对比 Base64:Base64 含 + 和 /,在 URL 中需转义,不适合做短链

最终流程

长 URL → MurmurHash → 32位整数 → 转无符号 long → Base62 编码 → 5~6位短码

核心代码

首先,在 Maven 项目中引入 Google Guava 库(提供了稳定高效的 MurmurHash 实现)

<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->  
<dependency>  <groupId>com.google.guava</groupId>  <artifactId>guava</artifactId>  <version>33.5.0-jre</version>  
</dependency>

哈希测试,看 MurmurHash 输出什么

// import com.google.common.hash.Hashing;
// import java.nio.charset.StandardCharsets;public static void main(String[] args) {  String url = "https://tse1-mm.cn.bing.net/th/id/OIP-C.wb-bFBTpIZDy_1jcvMY_5QHaE8?w=286&h=191&c=7&r=0&o=7&cb=ucfimg2&dpr=1.1&pid=1.7&rm=3&ucfimg=1";  int hash = Hashing.murmur3_32_fixed()  .hashString(url, StandardCharsets.UTF_8)  .asInt();  System.out.println("hash: " + hash); // 可能为负数,如 -904567778long unsignedHash = hash & 0xFFFFFFFFL; // 转为无符号 long,如 3390399518System.out.println("unsignedHash: " + unsignedHash);  
}
hash: -904567778
unsignedHash: 3390399518

Java 的 int 是有符号的,直接对负数做 Base62 编码会导致错误(比如模运算异常或空字符串),因此须先转为无符号 long

在很多业务中,同一个链接对不同用户可能有不同的行为或权限,就需要对短码进行区分生成,因此可以在生成短码时,将用户唯一标识(如 user_id、设备 ID)与原始 URL 拼接,再进行哈希:

create(url + "|" + userId)

因为输入变了,哈希结果就变了,短码自然也不同。

完整代码

带注释,放心抄

package io.jiangbyte.app.biz.urls.utils;  import com.google.common.hash.Hashing;  import java.nio.charset.StandardCharsets;  /**  * 1. 使用 Guava 的 Murmur3_32_fixed 哈希算法对输入字符串计算 32 位哈希值  * 2. 将有符号 int 转换为无符号 long(避免负数问题)  * 3. 将该数值使用 Base62 编码(字符集:0-9, A-Z, a-z)输出为紧凑字符串  */  
public class MurmurHashUtils {  /**  * Base62 编码字符集,按标准顺序排列  * - 数字 '0' 到 '9'(10 个)  * - 大写字母 'A' 到 'Z'(26 个)  * - 小写字母 'a' 到 'z'(26 个)  */  private static final char[] CHARS = buildBase62Chars();  /**  * Base62 的基数,值为 62  * 用于进制转换计算  */  private static final int BASE = CHARS.length;  /**  * 构建 Base62 字符数组  * 按照标准顺序依次填充数字、大写字母、小写字母  *  * @return 长度为 62 的字符数组,索引即对应数值(如 CHARS[0]='0', CHARS[10]='A')  */  private static char[] buildBase62Chars() {  char[] chars = new char[62];  int index = 0;  // 填充数字 '0' ~ '9'        for (char c = '0'; c <= '9'; c++) {  chars[index++] = c;  }  // 填充大写字母 'A' ~ 'Z'        for (char c = 'A'; c <= 'Z'; c++) {  chars[index++] = c;  }  // 填充小写字母 'a' ~ 'z'        for (char c = 'a'; c <= 'z'; c++) {  chars[index++] = c;  }  return chars;  }  /**  * 将一个非负长整型数值转换为 Base62 编码字符串  * 不断对 BASE 取模获取最低位字符,再除以 BASE,直到数值为 0, 最后将字符序列反转,得到高位在前的标准表示  *  * @param n 待编码的非负 long 值  * @return Base62 编码后的字符串  */  private static String base62(long n) {  if (n == 0) {  return "0"; // 特殊情况:0 编码为 "0"        }  StringBuilder sb = new StringBuilder();  while (n > 0) {  sb.append(CHARS[(int) (n % BASE)]); // 取模得到当前最低位对应的字符索引  n /= BASE;  // 整除进入下一位  }  // 由于是从低位到高位追加,需反转得到正确顺序  return sb.reverse().toString();  }  /**  * 对输入字符串进行哈希并生成 Base62 短字符串。  * 使用 Murmur3_32_fixed 算法(Guava 提供的固定种子版本,保证跨 JVM 一致性)  * 将结果转为无符号 32 位整数,再进行 Base62 编码  *  * @param input 原始输入字符串  * @return Base62 编码的短字符串  */  public static String create(String input) {  // 使用 UTF-8 编码计算 Murmur3_32 哈希值(固定种子)  int hash = Hashing.murmur3_32_fixed()  .hashString(input, StandardCharsets.UTF_8)  .asInt();  // 将有符号 int 转换为无符号 long(避免负数导致 base62 逻辑异常)  // -1 → 0xFFFFFFFFL = 4294967295  long unsignedHash = hash & 0xFFFFFFFFL;  return base62(unsignedHash);  }  /**  * 生成带用户隔离的短链标识。  * 若提供 userId,则将 URL 与 userId 拼接后再哈希  * 使得同一 URL 对不同用户生成不同短链  * 若 userId 为 null,则退化为普通模式  *  * @param url    原始长链接  * @param userId 用户唯一标识  * @return 用户隔离或通用的 Base62 短字符串  */  public static String create(String url, String userId) {  if (userId != null) {  // 拼接格式:原始URL + 分隔符 "|" + 用户ID  return create(url + "|" + userId);  } else {  // 无用户隔离,直接哈希原始 URLreturn create(url);  }  }  
}

测试输出

public static void main(String[] args) {  String url = "https://tse1-mm.cn.bing.net/th/id/OIP-C.wb-bFBTpIZDy_1jcvMY_5QHaE8?w=286&h=191&c=7&r=0&o=7&cb=ucfimg2&dpr=1.1&pid=1.7&rm=3&ucfimg=1";  int hash = Hashing.murmur3_32_fixed()  .hashString(url, StandardCharsets.UTF_8)  .asInt();  System.out.println("hash: " + hash); // 有符号 int,逻辑出错,为空  String base62_hash = base62(hash);  System.out.println("base62_hash: " + base62_hash);  long unsignedHash = hash & 0xFFFFFFFFL;  System.out.println("unsignedHash: " + unsignedHash);  String base62_unsignedHash = base62(unsignedHash);  System.out.println("base62_unsignedHash: " + base62_unsignedHash);  System.out.println("create: " + create(url));  System.out.println("create: " + create(url, "1234"));  System.out.println("create: " + create(url, "1234"));  System.out.println("create: " + create(url, "12345"));  
}
hash: -904567778
base62_hash: 
unsignedHash: 3390399518
base62_unsignedHash: 3hRlnC
create: 3hRlnC
create: 4YmoRu
create: 4YmoRu
create: GsQoj

相关新闻

  • 【开题答辩全过程】以 基于Springboot的汽车4s服务管理系统的设计与实现为例,包含答辩的问题和答案
  • 书籍-无著《瑜伽师地论》
  • 移动端自动化测试该用谁?Open-AutoGLM与Cypress兼容性、性能、维护性终极PK,

最新新闻

  • 大模型应用后端底座设计:高并发场景下的推理服务架构
  • 一文厘清UART、RS232、RS485、I2C、SPI:从硬件接口到电气标准的实战辨析
  • 2026 年锦州厨卫屋顶防水修缮三家对比测评 吉修匠 99.8 分稳居榜首 - 吉修匠
  • ELK 日志分析平台与全链路追踪:从日志聚合到故障定位的工程实践
  • 综合能力实训笔记——2026.6.17
  • WeChatMsg终极指南:如何3步永久保存你的微信记忆?

日新闻

  • 信任的进化:技术实现详解——如何用JavaScript构建博弈论模拟器
  • Terrakube自定义工作流:如何集成OPA、Infracost等工具扩展IaC能力
  • grunt-concurrent快速入门:5分钟学会并行运行Grunt任务

周新闻

  • 3步解锁iOS设备:applera1n激活锁绕过完全指南
  • 39 2026 人工智能证书终极盘点,普通人选 AI 证书可以从这些方向入手
  • Redis 暴露公网有多危险?从端口检查到补救步骤

月新闻

  • 【总结】入门篇:50句话让你记住架构核心概念
  • WeChatMsg技术方案解析:实现Mac微信数据自主管理的完整解决方案
  • WeChatMsg:革新性微信数据备份方案,打造你的专属数字记忆库

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号