新闻详情

新闻详情

首页 / 资讯中心 / 详情

Java短链接生成工具实战:短码算法、存储跳转与防刷压测

发布时间:2026/9/28 16:31:12来源:尧图网络
Java短链接生成工具实战:短码算法、存储跳转与防刷压测
简介这是一套面向Java后端开发者与全栈学习者的短链接生成工具完整源码围绕链接管理、访问数据统计与AB测试三大场景展开适合作为课程设计、毕业设计或二次开发的基础项目。压缩包共280个文件约1.44MB其中187个Java源文件构成后端核心逻辑19个Vue组件与11个JavaScript文件负责前端交互另有11个XML、8个YAML及若干CSS、HTML、JSON等配置与静态资源整体结构清晰、分层明确。该工具支持将原始网页链接缩短为简洁美观的短链并记录每次访问的详细信息生成地区分布、设备类型等专业统计图表同时允许随时修改跳转目标、批量创建短链兼顾效率与灵活性。目前已有329人学习下载读者可从中获取完整的工程目录组织、前后端分离实现思路以及数据统计模块的落地方式便于快速理解短链系统的设计要点并在此基础上扩展功能。1. 短链接生成工具到底在解决什么问题你在群里发一条带十几个参数的推广链接用户点之前先被长度劝退运营在短信里塞链接70 个字符被截成两行还超预算后台日志里同一篇文章的分享来源五花八门统计口径对不上。短链接生成工具就是把这些长 URL 压成http://域名/abc123这种短码用户点短码时服务端 302 跳回原始地址同时把点击事件记下来。它不复杂但要做好需要处理发号、冲突、缓存、跳转性能、统计去重这几件事。这篇面向用 Java 做课程设计、做内部工具、或者想自己搭一套短链服务的开发者从表结构一路写到压测源码结构可以直接照着搭。适合已经会写 Spring Boot 增删改查、但没想清楚「短码怎么发才不撞、跳转怎么扛住并发」的人。2. 短码生成算法选型自增、哈希还是发号器短链接工具的核心不是跳转是短码怎么来。选错算法后面要么冲突不断要么被遍历爬取要么扩容时改不动。这一章把三种主流做法拆开给出可运行的 Java 实现和参数边界。2.1 三种短码生成方案对比与选型依据常见做法有三类数据库自增 ID 转 62 进制、对长 URL 做哈希取前几位、独立发号器号段或雪花转 62 进制。它们的差别不在代码量在冲突概率、可预测性和扩展性。方案短码长度冲突处理可被遍历扩展性适用场景自增 ID 转 62 进制4~6 位天然不冲突是顺序可猜单库瓶颈内部工具、量小哈希取前 N 位固定 6~8 位需查重重试否好公开服务发号器转 62 进制5~7 位天然不冲突是但可打散好中大型服务自增方案最省事但短码连续别人从1递增就能把全站链接爬一遍。哈希方案用 MurmurHash 或 MD5 取前 6 位冲突概率按生日悖论算6 位 62 进制空间约 568 亿存 100 万条时冲突概率约 0.09%可接受但必须做查重。发号器方案是折中号段模式每次取 1000 个 ID 缓存在本地宕机浪费一段但不影响正确性。我一般会选发号器 62 进制因为它既避免顺序遍历可以对 ID 做位混淆又不依赖哈希查重。下面给出发号器和编码的核心代码。public class ShortCodeGenerator { // 62 进制字符表顺序可自定义打乱后短码更不可猜 private static final char[] CHARS 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.toCharArray(); private static final int BASE CHARS.length; // 62 // 把自增 ID 转成 62 进制短码 public static String encode(long id) { StringBuilder sb new StringBuilder(); while (id 0) { // 取余得到当前位再整除进入下一位 sb.append(CHARS[(int) (id % BASE)]); id / BASE; } // 反转保证高位在前 return sb.reverse().toString(); } // 解码用于排查把短码还原成 ID public static long decode(String code) { long id 0; for (char c : code.toCharArray()) { id id * BASE indexOf(c); } return id; } private static int indexOf(char c) { for (int i 0; i BASE; i) { if (CHARS[i] c) return i; } throw new IllegalArgumentException(非法字符: c); } }encode的逻辑是不断取余拿到最低位字符再整除推进最后反转。BASE62决定了 6 位短码能表示 62^6 ≈ 568 亿个 ID够用很久。CHARS的顺序就是安全边界如果按0-9a-zA-Z顺序短码1、2、3对应 ID 1、2、3一眼看穿把字符表打乱或者对 ID 先做一次异或混淆再编码遍历难度立刻上去。decode不是给线上用的是排查「这个短码对应哪条记录」时的后悔药。2.2 号段发号器的落地实现与参数设置单机自增够用但多实例部署时每个实例各自自增会撞。号段模式让每个实例从数据库领一段 ID 区间本地内存自增用完再领。Component public class SegmentIdGenerator { private final AtomicLong current new AtomicLong(0); private volatile long maxId 0; private static final int STEP 1000; // 每次领 1000 个 Autowired private IdSegmentMapper mapper; // 对应一张 id_segment 表 public synchronized long nextId() { if (current.get() maxId) { // 号段用完去数据库领新的一段 // UPDATE id_segment SET max_id max_id #{step} WHERE biz short_url mapper.updateMaxId(STEP); long newMax mapper.selectMaxId(short_url); maxId newMax; current.set(newMax - STEP); } return current.incrementAndGet(); } }STEP1000是吞吐和浪费的平衡点太小则频繁访问数据库太大则实例宕机时浪费的 ID 多。1000 在 QPS 几百的场景下一台实例几秒才领一次数据库压力可忽略。synchronized保证单实例内线程安全多实例靠数据库行锁保证号段不重叠。注意updateMaxId和selectMaxId要在同一事务里否则并发下可能读到中间态。如果 QPS 上万把STEP提到 10000或者换雪花算法彻底去掉数据库依赖。3. 存储与跳转链路从建表到 302 响应短码有了接下来是存哪、怎么查、怎么跳。这一层决定跳转延迟和统计能不能做准。3.1 表结构设计与索引取舍核心两张表短链表和点击记录表。短链表要支持按短码查原始 URL这是跳转热路径必须走索引。CREATE TABLE short_url ( id BIGINT NOT NULL AUTO_INCREMENT, short_code VARCHAR(10) NOT NULL COMMENT 短码, origin_url VARCHAR(2048) NOT NULL COMMENT 原始长链接, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, expire_time DATETIME NULL COMMENT 过期时间NULL 表示永久, status TINYINT DEFAULT 1 COMMENT 1 正常 0 禁用, PRIMARY KEY (id), UNIQUE KEY uk_short_code (short_code) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE click_log ( id BIGINT NOT NULL AUTO_INCREMENT, short_code VARCHAR(10) NOT NULL, click_time DATETIME DEFAULT CURRENT_TIMESTAMP, ip VARCHAR(45) NULL, user_agent VARCHAR(512) NULL, referer VARCHAR(512) NULL, PRIMARY KEY (id), KEY idx_code_time (short_code, click_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;uk_short_code唯一索引是跳转查询的关键WHERE short_code ?走它等值查询毫秒级。origin_url给 2048 是因为浏览器和部分平台对 URL 长度上限就在 2000 出头再长就该报错而不是截断。click_log的idx_code_time联合索引服务于「查某短码某时间段的点击」统计页按天聚合时能命中。注意click_log会快速膨胀单条短码被点十万次就是十万行生产上要么按天分表要么先写消息队列再异步落库别在跳转请求里同步 insert。3.2 跳转接口的缓存与 302 实现跳转是读多写极少缓存命中率能到 99% 以上。用 Redis 缓存短码到原始 URL 的映射未命中再查库并回填。RestController public class RedirectController { Autowired private StringRedisTemplate redis; Autowired private ShortUrlMapper mapper; GetMapping(/{code}) public void redirect(PathVariable String code, HttpServletResponse response) throws IOException { // 先查缓存key 加前缀避免和其他业务冲突 String cacheKey short:url: code; String origin redis.opsForValue().get(cacheKey); if (origin null) { // 缓存未命中查库 ShortUrl record mapper.selectByCode(code); if (record null || record.getStatus() 0) { response.sendError(404, 短链接不存在或已禁用); return; } // 检查过期 if (record.getExpireTime() ! null record.getExpireTime().before(new Date())) { response.sendError(410, 短链接已过期); return; } origin record.getOriginUrl(); // 回填缓存设 1 小时过期避免冷数据长期占用 redis.opsForValue().set(cacheKey, origin, 1, TimeUnit.HOURS); } // 302 临时跳转不缓存跳转关系方便后续改目标 response.setStatus(302); response.setHeader(Location, origin); } }用 302 而不是 301 是血泪经验301 会被浏览器和中间层长期缓存你后面想改这个短码指向的地址用户端根本不回源改了个寂寞。302 每次回源统计才准改目标才生效。缓存 TTL 设 1 小时是防止某个短码被改后长时间读到旧值如果业务允许可以缩短到 5 分钟。Location头直接放原始 URL注意原始 URL 里如果有中文要先 URL 编码否则部分客户端会乱码。跳转接口不要做重定向到中间页再跳多一跳就多一次延迟和一次统计丢失。4. 点击统计与防刷别让数据变成黑匣子短链接的价值一半在跳转一半在数据。但统计做不好要么漏记要么被刷成假数据运营拿着报表做决策就是灾难。4.1 异步埋点与去重策略跳转请求里同步写click_log会拖慢响应正确做法是发消息异步落库。同时要处理同一用户短时间重复点击的去重。// 跳转成功后发送点击事件不阻塞响应 private void recordClick(String code, HttpServletRequest request) { ClickEvent event new ClickEvent(); event.setShortCode(code); event.setIp(getClientIp(request)); event.setUserAgent(request.getHeader(User-Agent)); event.setReferer(request.getHeader(Referer)); event.setClickTime(System.currentTimeMillis()); // 去重同一 IP 短码 5 秒内只记一次 String dedupKey click:dedup: code : event.getIp(); Boolean first redis.opsForValue() .setIfAbsent(dedupKey, 1, 5, TimeUnit.SECONDS); if (Boolean.TRUE.equals(first)) { rocketMQTemplate.convertAndSend(click-topic, event); } }setIfAbsent是 Redis 的原子操作只有第一次设置成功才返回 true天然适合去重。5 秒窗口是经验值用户手抖连点、页面自动重试都在这个范围内正常二次访问间隔通常大于 5 秒。getClientIp要按X-Forwarded-For取第一个非 unknown 的地址否则拿到的全是网关 IP去重会把所有人当成一个人。消息队列用 RocketMQ 或 Kafka 都行消费端批量写库把随机 IO 变成顺序写。4.2 短码被遍历和恶意刷量的排查短码连续可猜时会有人写脚本从aaaa遍历到zzzz把你的库和统计全刷一遍。现象是某段时间click_log暴涨、大量 404、IP 集中在少数几个段。排查步骤先按 IP 聚合看请求量再按短码看是否集中在连续区间。-- 找出 1 分钟内请求超过 100 次的 IP SELECT ip, COUNT(*) AS cnt FROM click_log WHERE click_time DATE_SUB(NOW(), INTERVAL 1 MINUTE) GROUP BY ip HAVING cnt 100 ORDER BY cnt DESC;解决分三层短码字符表打乱让遍历不连续网关层对单 IP 限流令牌桶比如 100 次/分钟对不存在的短码返回统一 404 且不记录明细避免被当成探测信号。如果已经上线且短码是连续的别慌加一层布隆过滤器把存在的短码放进去不存在的直接拦掉不用查库。5. 避坑与常见问题排查这一章是踩过的坑合集每条按现象、原因、解决写照着排查能省不少时间。短码冲突导致插入失败。现象是日志里频繁出现Duplicate entry for key uk_short_code。原因是哈希方案没做查重或者发号器多实例号段重叠。解决哈希方案插入前先SELECT查一次冲突则对原 URL 加盐重哈希发号器方案检查数据库行锁是否生效updateMaxId必须带WHERE biz ?且在同一事务。跳转 302 变成 301 后改不动。现象是改了原始 URL用户访问还是旧地址。原因是某处用了 301 或者 CDN 缓存了跳转。解决全局搜setStatus(301)改回 302CDN 上对短链域名配置不缓存或短 TTL浏览器端让用户强刷。统计数字比实际点击数大很多。现象是报表点击数远超真实用户。原因是没去重或者爬虫、预加载把链接点了一遍。解决加 IP短码时间窗去重识别User-Agent里的爬虫关键字如bot、spider单独标记不计数对HEAD请求不记点击。Redis 缓存和数据库不一致。现象是禁用某短码后用户还能跳转。原因是缓存没删TTL 还没到。解决禁用/修改短码时主动redis.delete(cacheKey)别只依赖 TTL如果用了多级缓存本地缓存也要失效或者本地缓存 TTL 设得更短。长 URL 超长被截断。现象是跳转后 404原始 URL 少了尾巴。原因是origin_url字段长度不够或前端传参被截。解决字段给到 2048入库前校验长度超长直接返回错误让用户换链接别静默截断。6. 压测验证与短码长度调优功能跑通只是及格短链接工具真正的考验是并发跳转。这一章讲怎么验证以及短码长度这个参数怎么定。压测用 JMeter 或 wrk 都行目标是测跳转接口在缓存命中下的 QPS 和 P99 延迟。先造 10 万条短链数据用脚本随机取短码请求。# 用 wrk 压测跳转接口12 线程 400 连接持续 30 秒 wrk -t12 -c400 -d30s --latency http://localhost:8080/abc123看三个指标QPS 是否随连接数线性增长到瓶颈、P99 延迟是否稳定在 50ms 内、错误率是否为 0。如果 QPS 上不去先看 Redis 连接池够不够默认 8 个连接在 400 并发下必然排队把lettuce或jedis池调到 50 以上再看 Tomcat 线程数默认 200高并发下要调server.tomcat.threads.max。如果 P99 抖动大多半是缓存击穿给热点短码加本地缓存CaffeineTTL 几秒能压平毛刺。短码长度不是拍脑袋定的。6 位 62 进制有 568 亿空间按每天新增 10 万条算能用 150 年完全够。但如果你要做「短码可读、带业务含义」比如promo2024那长度就得按业务词来此时唯一性靠数据库约束保证。我的习惯是纯随机短码用 6 位带前缀的用「前缀4 位随机」总长控制在 10 位以内超过 10 位用户记不住短链的意义就弱了。最后说个验证技巧上线前用decode把一批短码还原成 ID看是否连续。如果连续说明字符表没打乱遍历风险还在赶紧改字符表顺序重新发码。这个检查我每次上线都做比事后被刷了再补救省心得多。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

绍兴实验室家具与设备企业实验室靠谱商家测评排名:选型不踩坑指南 2026/9/28 17:18:52

绍兴实验室家具与设备企业实验室靠谱商家测评排名:选型不踩坑指南

基础认知:什么是实验室家具与设备,核心功能是什么?很多初次接触实验室建设的客户,容易把实验室家具等同于普通办公家具,觉得不过是定制几张桌子、几个柜子而已。实际上,实验室家具与设备是专门为实验室操作场景设计的…

阅读更多 →
Claude插件化金融Agent工作区:Cowork与Managed Agents API实战 2026/9/28 17:18:45

Claude插件化金融Agent工作区:Cowork与Managed Agents API实战

1. 从"financial-services"这个标题说起:一个被低估的插件化落地场景第一次看到financial-services这个项目标题,加上Claude、Cowork、Managed Agents API、plugin这几个关键词,我脑子里第一反应不是"又一个金融数据接口封装&…

阅读更多 →
从启发式特征到机器学习:构建钓鱼网站检测系统实战 2026/9/28 17:18:45

从启发式特征到机器学习:构建钓鱼网站检测系统实战

简介:面向网络安全初学者与Python开发者,这套基于启发式特征的钓鱼网站检测系统,以域名结构、URL关键字、网页内容相似度等可量化特征为切入点,结合朴素贝叶斯、随机森林等分类算法,为识别钓鱼站点提供了可落地的代码方…

阅读更多 →
Kubernetes 上 agentic 工作负载的运行时编排:从容器运行时到模型运行时 2026/9/28 17:18:45

Kubernetes 上 agentic 工作负载的运行时编排:从容器运行时到模型运行时

1. 从“ax”这个标题说起:一个被低估的运行时编排切口第一次看到“ax”这个标题,很多人会以为是某个命令行工具的缩写,或者某个前端框架的别名。但把热搜词摊开来看——agentic、orchestration、runtime、Kubernetes、ax调度、agentic rag、c…

阅读更多 →
基于Python的舆情监控系统:情感分析与可视化实战 2026/9/28 17:18:45

基于Python的舆情监控系统:情感分析与可视化实战

简介:这是一套基于Python实现的舆情监控分析与预测系统完整项目源码,面向计算机、电子信息工程、数学等专业的大学生,适用于课程设计、期末大作业或毕业设计参考,也可作为数据分析与自然语言处理方向的实战练手项目。资源包共118个…

阅读更多 →
Boost PFC环路补偿设计:从传递函数到全工况稳定 2026/9/28 17:18:39

Boost PFC环路补偿设计:从传递函数到全工况稳定

1. 为什么PFC的环路补偿总在"勉强能用"和"突然振荡"之间反复横跳做电源设计这些年,Boost PFC应该是大家交手最多的拓扑之一。但说实话,真正能把PFC环路调明白的人,比能把LLC算明白的人少得多。原因不复杂——PFC的传递函…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉