ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Java短链接生成工具实战:从长URL到6位短码的落地与避坑

2026/9/28 16:31:07 拓冰建站 浏览量
Java短链接生成工具实战:从长URL到6位短码的落地与避坑 简介这是一套基于Java实现的短链接生成工具完整源码面向具备一定Java与前端基础的开发者、课程设计或毕业设计人群用于解决长链接管理繁琐、访问数据难以追踪的问题。项目融合Java、Vue、JavaScript、CSS与HTML等技术覆盖后端服务与前端界面可支撑链接缩短、访问统计与AB测试等典型场景。压缩包共280个文件约1.44MB其中187个Java源文件构成核心业务逻辑19个Vue组件与11个JavaScript文件负责前端交互另有PNG、SVG等图片资源及XML、YAML、JSON等配置与数据文件目录结构清晰便于按模块阅读与二次开发。已有329人学习下载。功能上支持将原始网页链接生成简洁美观的短链接记录每次访问的详细信息并生成地区分布、设备信息等统计图表还可随时修改跳转目标、批量创建链接适合作为链接管理与数据统计方向的学习范本或项目原型。1. 短链接生成工具从长 URL 到 6 位短码Java 方案到底怎么落地一条带十几个查询参数的推广链接扔进短信里直接占掉两行半用户点不点先不说渠道统计基本没法看。短链接生成工具要解决的就是这件事把任意长度的 URL 压成域名 6 位短码的形式用户访问短链时 302 跳回原地址同时把点击次数、来源、时间记下来。这个方向在 Java 技术栈里落地非常成熟核心无非三块——发号器、映射存储、跳转服务。适合谁做一是想拿它练手的 Java 学习者二是需要给营销、短信、二维码场景做渠道追踪的团队。源码层面一个能跑的最小实现不超过 500 行但要做到高并发下不重号、不雪崩参数和选型就得认真抠。下面按「先立住原理、再动手复现、最后讲坑」的顺序拆开讲。2. 短码怎么发自增 ID、哈希与 Base62 的三条路线短链接工具的第一个技术决策不是写代码而是选发号策略。短码本质是一个唯一标识怎么生成它直接决定了后面会不会重号、能不能预测、扩容难不难。常见做法有三条数据库自增 ID 转 Base62、URL 哈希取前几位、以及预生成号段。三条路线各有适用边界选错了后期改造成本很高。2.1 自增 ID Base62最稳的默认方案自增 ID 的好处是绝对不重号数据库帮你保证了唯一性。拿到 ID 之后转成 62 进制0-9、a-z、A-Z6 位 Base62 能表示 62^6 ≈ 568 亿个组合够绝大多数业务用很久。转换逻辑很短public class Base62 { private static final String CHARS 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; private static final int BASE CHARS.length(); // 62 // 将自增 ID 转为短码num 必须为正数 public static String encode(long num) { StringBuilder sb new StringBuilder(); while (num 0) { int rem (int) (num % BASE); sb.append(CHARS.charAt(rem)); num / BASE; } return sb.reverse().toString(); } // 短码还原为 ID用于反查 public static long decode(String code) { long num 0; for (char c : code.toCharArray()) { num num * BASE CHARS.indexOf(c); } return num; } }encode里先取余再整除最后reverse是因为低位先算出来。decode是逆运算用于从短码反推 ID。参数上唯一要注意的是num从 1 开始0 会返回空串。这个方案落地时数据库用AUTO_INCREMENT主键插入拿到 ID 后再回写短码字段或者干脆只存 ID、跳转时实时算短码——后者省一次更新但每次跳转多一次计算量不大时完全可接受。2.2 哈希方案为什么 MD5 取前 6 位容易翻车有人图省事直接对原始 URL 做 MD5取前 6 位当短码。这条路线的致命问题是哈希冲突。6 位十六进制只有 16^6 ≈ 1677 万种组合按生日悖论存到几万条时冲突概率就明显上升。冲突了怎么办加盐重算或者往后顺延取位——但这样同一个 URL 每次生成的短码可能不一样失去了「同 URL 复用短码」的能力。如果业务要求同一长链永远返回同一短码哈希方案必须配一张url_hash - code的唯一索引表冲突时用INSERT ... ON DUPLICATE KEY兜底。我的经验是除非你明确需要「相同 URL 同码」且能接受冲突重试否则别用哈希自增 ID 更省心。2.3 号段模式与分布式发号多实例部署时的取舍单机自增 ID 在分布式下会撞车。常见解法是号段模式数据库里存一个biz_tag和max_id每个实例一次申请 1000 个号内存里发完再申请下一段。这样数据库压力从「每次插入」降到「每千次插入」代价是实例重启会浪费掉当前号段里没用完的 ID——对短链接来说无所谓ID 不连续不影响功能。另一种是雪花算法但雪花 ID 是 64 位长整型转 Base62 后是 11 位左右短码变长二维码密度上升扫码识别率会下降。所以短链接场景我更倾向号段模式短码稳定在 6 位以内。提示号段长度别设太大1000 到 5000 之间比较合适。设成 10 万实例挂掉时浪费的号段会让短码位数提前增长。3. 存储与跳转MySQL 建表、Redis 缓存与 302 的取舍发号解决的是「短码从哪来」这一章解决「短码存哪、怎么查得快、跳转用什么状态码」。短链接是典型的读多写少场景读写的比例可能到 1000:1所以缓存设计比表结构更影响性能。3.1 表结构设计三个字段就够但索引别省最小可用的表结构如下CREATE TABLE short_link ( id BIGINT NOT NULL AUTO_INCREMENT, short_code VARCHAR(10) NOT NULL, origin_url VARCHAR(2048) NOT NULL, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, expire_time DATETIME NULL, PRIMARY KEY (id), UNIQUE KEY uk_short_code (short_code) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;short_code上的唯一索引是必须的它既保证不重号也是跳转查询的入口。origin_url给 2048 是因为部分电商链接带大量追踪参数超过 2048 的极少真遇到就截断或拒绝。expire_time允许为空表示永不过期。注意别在origin_url上建索引长文本索引又大又慢查询永远走short_code。3.2 Redis 缓存策略缓存穿透和雪崩怎么防跳转查询的路径是拿短码查 Redis命中直接返回未命中查 MySQL回填 Redis。这里有两个经典问题。缓存穿透——有人拿不存在的短码疯狂请求每次都打到数据库。解法是查不到时往 Redis 写一个空值设短过期时间比如 60 秒。缓存雪崩——大量短码同时过期请求全压到数据库。解法是过期时间加随机抖动// 回填缓存时基础 1 小时 0~300 秒随机避免同时失效 int ttl 3600 new Random().nextInt(300); redisTemplate.opsForValue().set(sl: code, originUrl, ttl, TimeUnit.SECONDS);sl:是键前缀方便后续按前缀清理或统计。TTL 设 1 小时是权衡太长则原链接改了短链还指向旧地址太短则缓存命中率下降。如果业务允许改链可以在更新时主动删缓存而不是等过期。3.3 302 还是 301一个影响统计的决策跳转用 301 还是 302很多人随手写 302 却没想过为什么。301 是永久重定向浏览器会缓存第二次访问根本不经过你的服务器——这意味着点击统计会丢。302 是临时重定向每次访问都回源统计完整。短链接的核心价值之一就是渠道数据所以默认用 302。只有当你确定这个短链永不改目标、且不需要统计时才考虑 301 省服务器流量。Spring Boot 里一行搞定GetMapping(/{code}) public ResponseEntityVoid redirect(PathVariable String code) { String url shortLinkService.resolve(code); // 内部走缓存 if (url null) { return ResponseEntity.notFound().build(); } return ResponseEntity.status(HttpStatus.FOUND) // 302 .header(HttpHeaders.LOCATION, url) .build(); }HttpStatus.FOUND就是 302。LOCATION头带原始 URL。注意resolve返回 null 时要给 404别抛异常否则日志会被无效短码刷爆。4. 避坑与排查短链接上线后最容易翻车的五件事前面讲的是「怎么搭起来」这一章讲「搭起来之后哪里会炸」。短链接看着简单但线上出问题时往往很隐蔽下面五条是我踩过或见别人踩过的。4.1 短码大小写敏感导致 404现象生成的短码里有大写字母用户手动输入时全打成小写跳转 404。原因Base62 区分大小写aB3和ab3是两个不同短码。解决要么生成时只用小写字母加数字Base36牺牲一部分容量换容错要么在查询时对短码做规范化但规范化会引入歧义不推荐。我的做法是 Base62 生成、但对外展示和输入都按原样处理同时在短链域名后加提示。如果业务面向普通用户手动输入直接用 Base36 更稳。4.2 长 URL 未做协议校验跳转到 javascript 伪协议现象有人提交javascript:alert(1)当原始 URL短链跳转后执行脚本。原因生成时没校验 URL 协议。解决入库前用URI解析只允许http和https两种 scheme其他一律拒绝。这一步必须在服务端做前端校验可以被绕过。public static boolean isValidUrl(String url) { try { URI uri new URI(url); String scheme uri.getScheme(); return http.equalsIgnoreCase(scheme) || https.equalsIgnoreCase(scheme); } catch (URISyntaxException e) { return false; } }4.3 缓存与数据库不一致改了原链接还跳旧地址现象运营改了短链指向用户访问还是旧地址。原因更新了 MySQL 但没删 Redis缓存里还是旧值要等 TTL 过期。解决更新操作里先更新数据库再删除缓存不是更新缓存。删除失败就重试或者用延迟双删。别用「更新缓存」策略并发下容易写进脏数据。4.4 号段模式重启后短码跳变被误认为 bug现象服务重启后新生成的短码突然从aB3跳到xY9运营以为数据错乱。原因号段模式重启会丢弃内存里没用完的 ID新号段从数据库当前max_id继续。解决这不是 bug是设计取舍。如果业务方在意连续性可以在重启时把没用完的号段写回数据库但会增加复杂度。我的建议是提前跟业务方说清楚短码不保证连续。4.5 高并发下重复插入触发唯一索引异常现象压测时偶发DuplicateKeyException。原因并发请求拿到同一个 ID号段边界处理有 bug或者哈希方案冲突重试逻辑没写好。解决捕获唯一索引异常后重试一次重试时重新发号。同时检查号段模式的并发控制AtomicLong的getAndIncrement要保证号段切换时的原子性别用普通long加锁。注意重试次数别超过 2 次否则可能是发号器本身有问题继续重试只会放大故障。5. 进阶技巧用布隆过滤器挡无效短码把 404 拦在数据库之前前面第四章讲了缓存穿透用空值兜底但空值方案有个前提——你得先查一次数据库才知道「不存在」。如果攻击者用随机短码高频请求每次都要查库再写空值数据库压力依然存在。更彻底的做法是在 Redis 前面加一层布隆过滤器把所有已生成的短码放进去查询时先过布隆过滤器判定「一定不存在」的直接返回 404连 Redis 都不用查。布隆过滤器的特点是说不存在就一定不存在说存在可能误判。误判率可以通过调整位数组大小和哈希函数个数控制。用 Guava 的实现预估 1000 万条数据、误判率 1%// expectedInsertions 预估元素数量fpp 可接受的误判率 BloomFilterString bloomFilter BloomFilter.create( Funnels.stringFunnel(StandardCharsets.UTF_8), 10_000_000L, 0.01); // 生成短码后加入 bloomFilter.put(shortCode); // 查询时先判断 if (!bloomFilter.mightContain(code)) { return null; // 一定不存在直接 404 } // 可能存在继续走 Redis - MySQL参数上10_000_000L是预估总量设小了误判率会飙升0.01是 1% 误判率设成 0.001 会显著增加内存占用。布隆过滤器的位数组大小和元素数量成正比1000 万条 1% 误判大约占十几 MB可以接受。要注意的是布隆过滤器不支持删除短链如果会过期删除得用计数布隆过滤器或者定期重建。重建时用双缓冲新过滤器在后台构建构建完原子替换避免重建期间请求全部穿透。另一个进阶点是短码的防猜测。自增 ID 转 Base62 的短码是连续的aB3后面就是aB4有人可以遍历你的短链。如果业务对隐私敏感可以在 ID 转 Base62 之前做一次可逆混淆比如异或一个固定盐再转这样短码看起来随机但依然能反解。盐值放配置文件别硬编码在代码里。验证这套方案是否达标我会做三件事一是用 JMeter 压 1000 并发读看 P99 是否在 10ms 以内二是用随机短码刷 10 万次确认数据库 QPS 没有明显上升三是手动改一条原链接确认跳转立即生效。这三步过了基本可以上线。我自己做这类工具最大的教训是别一上来就追求分布式和高可用先用单机 MySQL 加 Redis 把功能跑通把短码规则、过期策略、统计口径定死再考虑扩容。很多翻车不是因为性能不够而是因为短码规则中途改了导致老链接全废。希望帮到你。本文还有配套的精品资源点击获取